I've got an angular app like:
angular.module('ngStyleApp', [])
.controller('testCtrl', function($scope) {
$scope.list = [1,2,3];
$scope.getStyles = function(index) {
console.log('getting styles for index ' + index);
return {
color: 'red'
};
};
});
with the corresponding markup:
<div ng-app="ngStyleApp">
<ul ng-controller="testCtrl">
<li ng-repeat="value in list" ng-style="getStyles($index)">
{{value}}
</li>
</ul>
</div>
The visible output is three red list items, as expected. But the statement is logged to the console a total of 6 times, implying that the view is rendered twice:
getting styles for index 0
getting styles for index 1
getting styles for index 2
getting styles for index 0
getting styles for index 1
getting styles for index 2
Why?
I've got an angular app like:
angular.module('ngStyleApp', [])
.controller('testCtrl', function($scope) {
$scope.list = [1,2,3];
$scope.getStyles = function(index) {
console.log('getting styles for index ' + index);
return {
color: 'red'
};
};
});
with the corresponding markup:
<div ng-app="ngStyleApp">
<ul ng-controller="testCtrl">
<li ng-repeat="value in list" ng-style="getStyles($index)">
{{value}}
</li>
</ul>
</div>
The visible output is three red list items, as expected. But the statement is logged to the console a total of 6 times, implying that the view is rendered twice:
getting styles for index 0
getting styles for index 1
getting styles for index 2
getting styles for index 0
getting styles for index 1
getting styles for index 2
Why?
The Angular $digest
cycle evaluates the ngStyle attribute at least twice - once to get the value and once to check if it has changed. It actually keeps iterating until the value settles so could potentially check the value many times.
Here's a picture to illustrate this:
Here is a good blog post illustrating this: angular digest blog
Actually, try this quote from StackOverflow that says it very well:
When watched functions are evaluated (during $digest) if any of them have changed from the previous $digest then Angular knows that change might ripple through to other watched functions (perhaps the changed variable is used in another watched function). So every watch is re-evaluated (also called dirty processing) until none of the watches results in a change. Thus typically you'll see 2 calls to watched functions per digest and sometimes more (up to 10- at 10 loops through it gives up and reports an error saying it can't stabilize).
(Reference here)
This is the code of the ngStyle
directive:
var ngStyleDirective = ngDirective(function(scope, element, attr) {
scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
if (oldStyles && (newStyles !== oldStyles)) {
forEach(oldStyles, function(val, style) { element.css(style, '');});
}
if (newStyles) element.css(newStyles);
}, true);
});
Notice that there is a scope.$watch
for attr.ngStyle
, that watch is what is making it trigger twice.
For instance, if you try to do the same thing using ngInit
, you will notice that the function only gets called once. Now, lets have a look at the code of the ngInit
directive, it looks like this:
var ngInitDirective = ngDirective({
priority: 450,
pile: function() {
return {
pre: function(scope, element, attrs) {
scope.$eval(attrs.ngInit);
}
};
}
});
Notice that there is no watch
in this directive.