Paul
Paul

Reputation: 61

Function calls to controllers under AngularJS ng-repeat become infinite

I am just trying to get some proof of concept working for AngularJS. In the below code, the call to getClass is going to an infinite loop for some reason. The scope variable noOfCalls is set to increment in the function. My expectation was Angular to call this function once for every iteration. Anyone can help?

Plunker link http://plnkr.co/edit/7QvkSEQGTJtExwYm9kgD?p=preview

HTML

<body ng-controller="MainCtrl">
<h1>Total number of calls: {{noOfCalls}}</h1>
<div class="container" ng-repeat="item in arraydata">
<div class="form-group">
<input type="text" class={{getClass(item)}} ng-model=item.name />
</div>
</div>
</body>

Controller

var app = angular.module('angularjs-starter', []);

app.controller('MainCtrl', function($scope) {
   $scope.arraydata = [];
   $scope.arraydata.push({id:1, name: "test1"});
   $scope.arraydata.push({id:2, name: "test2"});
   $scope.arraydata.push({id:3, name: "test3"});
   $scope.noOfCalls=0;

   $scope.getClass = function(item)
   {
    $scope.noOfCalls+=1;
    return("form-control");
    }
});

Upvotes: 1

Views: 1704

Answers (2)

runTarm
runTarm

Reputation: 11547

The root cause is as @Chandermani answered that all the expressions will be rerun repeatedly until no change has detected.

For an educational purpose only, if you really want to do something like that, you have to somehow find a way to break the loop. For example.

var childScope = $scope.$new();
childScope.noOfCalls = 0;

$compile($element.find('h1'))(childScope);

$scope.getClass = function(item) {
  setTimeout(function () {
    childScope.noOfCalls++;
    childScope.$digest(); // use $digest here to not trigger digest cycle of parent scopes
  }, 0);

  return("form-control");
};

Example plunker: http://plnkr.co/edit/d4TB6sh4Sc7MP7FrQyOV?p=preview

The tricks are:

  1. create a new scope and compile the h1 tag with it and store the noOfCalls flag in this new scope.
  2. using setTimeout to defer the increment of noOfCalls until the digest cycle has finished.
  3. call childScope.$digest() to update the h1 in the views but avoid a new digest cycle of parent scopes to be triggered.

Upvotes: 0

Chandermani
Chandermani

Reputation: 42669

The number of times an interpolation is evaluated is not controller by you but the framework. Angular calls your function numerous times during a process called digest cycle. This process is used to determine if model has changed for elements that are bound.

In your case you increment noOfCalls in a function that is define inside the interpolation {{getClass()}}. This results in changing of noOfCalls variable which is again used in interpolation {{noOfCalls}}. The digest cycle keeps running or re evaluating your expression till the model is stable or 10 iterations are reached. In the above case model keeps updating again and again with getClass() evaluation.

Remove the interpolation {{noOfCalls}} and the iteration would not happen. If you call functions in interpolation or directives that require evaluation of expression, keep the function side affect free.

Read about the digest cycle in developer guide here https://docs.angularjs.org/guide/scope

Upvotes: 2

Related Questions