Reputation: 1502
The following HTML code populates a ul with 21 phones:
<li ng-repeat="phone in phones" ng-class="{'digestTest': countDigestOccurences(phone) }">
<p>{{phone.snippet}}</p>
</li>
countDigestOccurences
is a JavaScript method which uses a dictionary to keep track of how many times countDigestOccurences()
is called per phone.
$scope.countDigestOccurences = function(phone){
var phoneFound = false;
$.each($scope.digestOccurencesPerPhone, function(){
if(this.phone.id == phone.id){
phoneFound = true;
this.occurences++;
}
});
if(!phoneFound)
{
$scope.digestOccurencesPerPhone.push({
phone: phone,
occurences: 1
});
}
}
Through this method I can clearly see that countDigestOccurences is called 4 times per phone. I can not, for the life of me, figure out why it's called 4 times.
Update:
Number of cycles will remain 4 even if the Phone item's HTML is as follows:
<li ng-repeat="phone in phones "
class="thumbnail phone-listing" ng-class="{ 'digestTest': countDigestOccurences(phone), 'digestTestAgain': randomMethodDoesNothing() }">
<p>{{phone.snippet}}</p>
</li>
Upvotes: 1
Views: 1198
Reputation: 156
when Angular compiles and see an expression on the view, like ng-class="function()"
, ng-model="toto"
, a $watch
is created for it. At every digest cycle, the watches are evaluated by the dirty checking to determine if there is any change in the model.
So in your ng-repeat, you have : one watcher on the phones collection, one watcher on each phone instance and one watcher on the function. As the function on the view isn't a scope variable, angular can't know if the result of the function has changed (you may affect an other scope variable in the function) and so, it reevaluate the function result for each digest cycle.
So you have phones + phone + function + last digest cycle to verify everithing it's ok : 4 cycles
A good practice is to not use function in the view except if rare cases. Instead, store the result of the function in a scope variable and render this variable in the view.
Update :
Due to the discussion bellow, note that only one watch si created for the ng-class directive and it correspond to the value of ng-class. I.e., with :
ng-class="{'toto' : functionOne(), 'titi' : functionTwo()}"
, the watch is on : {'toto' : functionOne(), 'titi' : functionTwo()}
.
Issued from the AngularJs directive code : scope.$watch(attr[name], ngClassWatchAction, true);
Upvotes: 4
Reputation: 1568
How many ajax calls did do you have via $http? Each of them would trigger a $digest. Also, if something changed (and it has, the new data arrived), another $digest will run to make sure it covers everything.
To avoid this add a boolean on an ng-if
on a parent element and set it to true once all ajax calls have arrived (see $q
).
Upvotes: 0