1

Why do I get in to a digetst loop when I repeat over a cloned array?

angular.module('myApp', [])
    .controller('Ctrl1', function(MyService) {
        var $ctrl = this;

        $ctrl.getData = MyService.getData;
    })
    .service('MyService', function () {
        var data = [
            {
             name: 'Adam',
                age: 12
            }, {
             name: 'Bob',
                age: 14
            }, {
             name: 'Caesar',
                age: 15
            }];
            
        this.getData = function () {
            // This is the offending line. If I remove the call to
            // angular.copy it works but in my real scenario I want to
            // modify the copy of `data` before returning it, without
            // affecting the original version. That's why I need to clone
            // it first.
            return angular.copy(data);
        };
    });
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.js"></script>
<div ng-app="myApp" ng-controller="Ctrl1 as $ctrl">
    <table border="1">
        <tr>
            <th>Name</th>
            <th>Age</th>
        </tr>
        <tr ng-repeat="data in $ctrl.getData()">
            <td>{{data.name}}</td>
            <td>{{data.age}}</td>
        </tr>
    </table>
</div>
Tobbe
  • 3,282
  • 6
  • 41
  • 53

2 Answers2

2

You used $ctrl.getData()(binding) on view which does return an data array copy using angular.copy. So what happens is when digest cycle start it evaluates $ctrl.getData() and returns data copy, dues to reference change in watcher(binding) value. Angular re-runs digest cycle(it happens till all the binding gets stable). Again it tries to evaluate $ctrl.getData() value get and new copy of data array. So this process goes on as every time you are changing the reference of binding variable. Since after reaching to 10th operation it throws an error (by default angular digest cycle TTL = 10).

How digest cycle?

  1. It goes through page collects all the bindings & put those expressions inside $$watchers array of $scope.
  2. It starts initial digest cycle to evaluate of bindings, & update corresponding model value on page.
  3. Digest cycle internally performs dirty checking, where it loop through all the $$watchers array & evaluate there bindings against $scope. Dirty checking meaning it check watcher expression currently evaluated value with its old value.
  4. If any changes are found in dirty checking then again it re-run digest cycle from top to bottom(from $rootScope to all childs).
  5. Until all the bindings gets stable this process happens.

To fix this error you have to maintain two copies of variable in your code.

Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
0

That error is coming because angular.copy() returns a new copy instance of the object. While angular is running the digest cycle , the scope is always dirty. The limit of digest iterations is 10. Thats why u are getting the digest limit reached error. Change your service to this, You are good to go.

this.getData = function () {
        return data;
    };
Sasank Sunkavalli
  • 3,864
  • 5
  • 31
  • 55
  • Yes. I know I get a new instance of the array (a clone/copy). What I don't understand is why Angular thinks it needs to re-run the digest cycle because of that. – Tobbe Sep 24 '16 at 11:50
  • And as I tried to explain in my comment in the code I can't just remove the call to `angular.copy` – Tobbe Sep 24 '16 at 11:50
  • For that u have to maintain two copies of data & return the second copy & modify it according to the requirement. But make sure u return the same object , not the new instance – Sasank Sunkavalli Sep 24 '16 at 12:01