I'm creating application using AngularJS + ngRoute module + Angular Bootstrap UI. I've created index.html page and few templates, that are being loaded through ng-view directive placed in index.html.
Currently I'm trying to make parallax effect on background images, based on this plugin:
https://github.com/brettdonohoo/angular-parallax
http://brettdonohoo.com/angular-parallax/index.html
In one of my views I have DIV element, that has parallax directive on it.
<div parallax-background parallax-offset="0.4" parallax-ratio="0.7" class="slide" style="background-image: url('{{covers[0].image}}')" ng-click="open(covers[0])"></div>
The problem is - when I'm loading application, the directive is fired correctly on the DIV element, but the background-position css does not change. When I scroll window - background-position changes OK. But I need the position to be applied on initial change too.
When I move my DIV element out of the template to the index.html it works. But of course I would like it to display only one one subpage.
My directive:
.directive('parallaxBackground', ['$window',
function ($window) {
return {
restrict: 'A',
transclude: true,
template: '<div ng-transclude></div>',
scope: {
parallaxRatio: '#',
parallaxOffset: '#',
},
link: function ($scope, elem, attrs) {
var setPosition = function () {
var calcValY = (elem.prop('offsetTop') - $window.pageYOffset) * ($scope.parallaxRatio ? $scope.parallaxRatio : 1.1) - ($scope.parallaxOffset ? $scope.parallaxOffset : 0) * $window.innerHeight;
elem.css('background-position', "50% " + calcValY + "px");
};
angular.element($window).bind('load', function (e) {
setPosition();
$scope.$apply();
});
angular.element($window).bind("scroll", setPosition);
} // link function
};
}
]);
Related
I'm trying to validate dynamically generated inputs, but I do not know how to do this.
When I add the div that triggers the directive and inserts the inputs dynamically the div adds the 'has-error' class but does not apply the input style, anyone knows the best way to do this that I'm trying to do?
Here is the markup:
<div ng-if="conditionItem.field.id"
ng-class="{true: 'has-error'}[conditionItem.field.hasError]"
dynamic
input-router
source="conditionItem.field"
ng-click="FieldConditionsCtrl.valueTest(conditionItem.field.hasError)"
ng-required="true"
ng-model="conditionItem.situation[$index]">
</div>
Here is the directive how generate the inputs:
(function() {
'use strict';
angular
.module('applicationInputs', ['rzModule', 'focus-if', 'ui.utils.masks'])
.directive('inputRouter', inputRouter);
/** #ngInject */
function inputRouter($compile){
return {
restrict: 'EA',
scope: {
ngModel: '=',
source: '=',
placeholder: '#',
tabIndex: '='
},
link: function(scope, element, attrs) {
var canvas = angular.element(element[0]);
scope.source.editable = angular.isUndefined(scope.source.editable) ? true : scope.source.editable === true;
//used by setting to override selected field
if (angular.isDefined(attrs.dynamic)) {
scope.$watch('source', function () {
var html = '<' + scope.source.type + 'input></' + scope.source.type + 'input>';
canvas.children().detach();
canvas.append($compile(html)(scope));
});
} else {
var html = '<' + scope.source.type + 'input></' + scope.source.type + 'input>';
canvas.append($compile(html)(scope));
}
}
}
}
})();
Here is my style:
.has-error {
border-color: red
}
Try border: 1px solid red; instead of just setting border-color. Border width is 0 by default, so just setting a color isn't enough
Just a couple nitpicky-subjective style comments also:
element is already a jqLite/jquery element, so no need to call angular.element
the scope.source.editable === true assignment can be shortened to !!scope.source.editable if you really want it to be a boolean.
While clever, this sort of jquery style element building is generally a code smell in angular js. If you really want to go this route, I would build self-contained directives for your inputs and use inputRouter's template to choose. It's easier to understand, so your future self will thank you
{true: 'has-error'}[conditionItem.field.hasError] took me a minute. Just write it as {conditionItem.field.hasError: 'has-error'}
I'm a big fan of these style guides for AngularJS:
John Papa - ES5, but good advice
Todd Motto - ES6+
There's overlap, but take what you like from each.
I have a custom angular directive that I conditionally display it, as it's inside ng-if. Inside my custom directive I display html elements, one of them having an absolute positioning.
I'd like to set the absolute element's width to be that of the directive's root width. Please note, I cannot set the directive root's display to be relative.
How would you advise I achieve such a feat?
UPDATE:
Here's a jsfiddle -
http://jsbin.com/qofolahani/edit?html,css,js,console,output
please note that in the link function I don't yet the element's css styling yet and thus width is equal to 0, which means I cannot set it in the child div's width.
Your issue is you use restrict: E.
Browser does not bind getCalculatedStyle method to non-standard DOM entities.
Change this directive to attribute using:
<div my-dir></div>
And directive:
{
restrict: 'A',
link : function (scope, element) {
element.addClass('my-dir');
element.find('.my-dir-content').width(element.width());
},
template: '<div class="my-dir-content"></div>'
}
Or you should use internal templated divs only.
replace: false can help with this.
{
restrict: 'E',
link : function (scope, element) {
var o = element.find('.my-dir');
var i = o.find('.my-dir-content');
i.width(o.width());
},
replace: false,
template: '<div class="my-dir"><div class="my-dir-content"></div>/div>'
}
Or otherwise, replace parent node:
{
restrict: 'E',
link : function (scope, element) {
var i = element.find('.my-dir-content');
i.width(element.width());
},
replace: true,
template: '<div class="my-dir"><div class="my-dir-content"></div>/div>'
}
JSBin Preview
Bonus:
You need a code like this to update width on window resize:
$(window).on('resize', onResize);
function onResize() {
i.width(o.width());
}
scope.$on('$destroy', function(){
$(window).off('resize', onResize);
});
You need to add a css style to the directive root and set this position to relative, something like
custom-directive {
position: relative;
}
Setting the root html element on position: relative will allow the inner html elements to take position absolute in reference to directive.
Then you can take the inner html element and set
inner-html-element {
position: absolute;
width: 100%;
}
I'm new to Angular JS and am trying to create a custom directive that creates img tags based on a list of urls and then performs some DOM manipulation afterward. I want to have the images fade in and out over time, like a slideshow. So far, I've got the images added to the DOM, but I can't find anywhere in the compile or link functions that allows me to manipulate the images, as the ng-repeat doesn't appear to have rendered yet. Any ideas?
The directive:
Logger.app.directive("ngSlideshow", function ($compile) {
return {
restrict: "A",
templateUrl: "templates/slideshow.html",
scope: {
pictures: "=ngSlideshow"
},
link: {
post: function(scope, element, attributes) {
var length = $(element).find("img").length; // 0
}
}
};
});
The template:
<div class="spinner">
<img src="{{picture}}" ng-repeat="picture in pictures" alt="" />
</div>
The view:
<div class="pictures" ng-slideshow="log.pictures"></div>
Thanks!
Chris
So I have an nvd3 graph in my index.html whose height is set to {{varyingHeight}} like so (code snippet):
<nvd3-line-plus-bar-chart data="data"
showXAxis="true" showYAxis="true"
tooltips="true" interactive="true"
showLegend="true"
height='{{varyingHeight}}'
>
</nvd3-line-plus-bar-chart>
Now in my directive, I have a code which identifies when the height change takes place, and what the new height is:
app.directive('test', function () {
return {
restrict: 'EA',
scope: {},
link: function (scope, element) {
scope.$on('split.resize', function() {
console.log('I got resized');
console.log(element.height());
});
}
};
});
In my controller, I now want to be able to set the new height like so:
$scope.$apply(function(){
$scope.varyingHeight = h;
})
I'm new to angularjs so I can't figure what the best way is to do this. I have seen answers which show how to do this the other way round, i.e from the controller to the directive but this hasn't helped me. How do I pass a element.height() to variable h from the directive to the controller? Or is my code structured wrong in the first place?
You do it by binding the height attribute to the value passed from the controllers scope. Here's an example: http://jsbin.com/vapipizu/1/edit
The important part is that you replace height="{{varyingHeight}}" with height="varyingHeight" and that your directive binds the height attribute like this:
scope: {
height: '='
}
I also faced this issue, I resolved it by passing the height as attribute to the directive
My sample directive
App.directive('aBc',function () {
return {
restrict : 'AE',
scope : {
gridHeight : '#'
},
template : '<div style= "height : {{gridHeight}}px" >'
+'<p>sdtyhdrtydrt-- {{gridHeight}} </p>'
+ '</div>'
};
});
pass the height through directive tag
directly you can pass the height
<div a-bc grid-height="200"></div>
<div a-bc grid-height="500"></div>
<div a-bc grid-height="1000"></div>
or you can set it from your controller
<div a-bc grid-height="someHeight"></div>
initialize someHeight in controller like
$scope.someHeight = 500;
How to attach arbitrary data to an html element declaratively, and retrieve it.
Please see the code. http://plnkr.co/edit/sePv7Y?p=preview
Angular has the jQuery data() support.
So, I want to attach data to each li element (say _data = node ) in the template, and later on to retrieve it using
var li = elm[0]....
console.log('li-', li.data('_data'))
li - {id:1}
Code:
'use strict';
var app = angular.module('Directives', []);
app.controller('MainCtrl', function ($scope) {
$scope.data = [
{id:1}, {id:2}, {id:3}
];
});
app.directive('test', function ($timeout) {
return {
template: '<li class="ch" ng-repeat="node in data">' +
'<span class="span2">' + 'id - {{node.id}}' + '</span>' +
'</li>',
restrict: 'A',
link: function (scope, elm, attrs) {
console.log(elm[0].children);
}
};
});
Edit:
Updated the code with how I like to set data.
template: '<li class="ch" ng-repeat="node in data" data-node="node">' +
couldn't select the li element properly now to see whether it is working
tried,
elm[0].children[0].data()
elm.children[0].data()
etc..
First of all, if it were some third party lib that you are trying to integrate with angular, that might be ok, but now you're generating DOM with angular and embedding data in the DOM. This is very strange.
Second, your test directive template uses ngRepeat, which creates isolate scope and you won't be able to access li items declaratively. You will have to use DOM traversal, which is also not very angular-way-ish.
Third, your view should be bound to model by angulars two-way bindings. Do not try to simulate opposite behaviour on top of that. Either you should not use angular or you should change your approach to your problem, because it will be pain to develop and maintain otherwise.
I would provide a real answer if you could describe what are you trying to achieve and why exactly do you need that model in data. Now the easiest solution would be ditching test directive and rewriting it as such:
controller's template:
<ul>
<li ng-repeat="node in data" model-in-data="node">
<span class="span2">id - {{node.id}}</span>
</li>
</ul>
directive modelInData
.directive('modelInData', function($parse) {
return {
restrict: 'A',
link: function($scope, $element, $attrs) {
var model = $parse($attrs.modelInData)($scope);
$attrs.$set('data', model);
}
}
});
Here each li element adds it's model to the data attribute.