1

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.

enter image description here

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>
11

2 Answers 2

4

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);

Sign up to request clarification or add additional context in comments.

7 Comments

If that were the case, wouldn't the code I put in the bottom of answer now increase the cycles from 4? Code isn't up to standards because this is a project testing digest cycle
I don't see why the code you added would increase the number of digests Lucas.
Updated again for clarity's sake. I have introduced a new function inside ng-class. Shouldn't this add another watcher and thus another cycle?
No, because you're always working on the same phone instance, even if you add phone.imageUrl, phone.id,... this is the phone object watched and not its attributes
Yes, however I have now added another function. Doesn't a function need another cycle from what I understood?
|
0

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).

2 Comments

None, this is static data. It's all a project to figure out the digest cycles.
Static data, but on the server side (API or json files). On UI, you fetch it by HTTP so it becomes "dynamic".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.