Halaster
Halaster

Reputation: 1502

function inside ng-repeat called 4 times (AngularJs)

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>

Upvotes: 1

Views: 1198

Answers (2)

matiou
matiou

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

Dragos Rusu
Dragos Rusu

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

Related Questions