Bite code
Bite code

Reputation: 596653

"10 $digest() iterations reached. Aborting!", but while $scope is NOT updated

I managed to create a small sample that works out of the box and can reproduce the issue. It's a filter that removes randomly 2 elements from an array:

<!DOCTYPE html>
<html ng-app='myApp'>
  <head>
    <meta charset="utf-8" />
    <script src="https://code.angularjs.org/1.6.5/angular.js"></script>
    <script>
    angular.module('myApp', [])
    .filter('random', function() {
        return function(input) {

            var a = Math.floor(Math.random()*input.length);
            var b = Math.floor(Math.random()*input.length);
            return input.filter(function(element, i){
                if (i !== a && i !== b){
                    return element;
                }
            });
        };
    })
    .controller('Controlleur', function($scope) {
        $scope.contacts = [
            {"name": "donatello", "tel": 12456},
            {"name": "michaelangelo", "tel": 12456},
            {"name": "leonardo", "tel": 12456},
            {"name": "raphael", "tel": 12456},
            {"name": "robert", "tel": 12456},
        ]
    });
    </script>
</head>
<body  ng-controller="Controlleur">
    <ul>
        <li ng-repeat="contact in contacts|random track by contact.name ">
            <strong>{{contact.name}}</strong>: {{contact.tel}}
        </li>
    </ul>
</body>
</html>

Copy/paste that in a file, load the file in a browser, and open the console. If you hit F5 a few number of times, you'll see the filter works but you'll randomly get:

Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: [[{"msg":"fn: regularInterceptedExpression","newVal":21,"oldVal":18},{"msg":"fn: regularInterceptedExpression","newVal":"raphael"},{"msg":"fn: regularInterceptedExpression","newVal":"12456"},{"msg":"fn: regularInterceptedExpression","newVal":"robert"},{"msg":"fn: regularInterceptedExpression","newVal":"12456"}],
...

The problem is that it usually means $digest is triggered too many times in a row. However, I don't update the scope explicitly anywhere. I'm merely creating a new array in a filter, with so apparent side effect.

Upvotes: 0

Views: 400

Answers (2)

Bite code
Bite code

Reputation: 596653

I actually found a better solution (for my use case) than @charlietfl's: cache the random result at the filter level in a closure, and return this instead the random result during the digest. I can just then bust the cache when I need a new random value, which I did anyway in a $interval.

I let him the "accepted answer" flag because he explains the why, not just the how.

Upvotes: 0

charlietfl
charlietfl

Reputation: 171679

Since you are returning random items in array every digest the scope never stabilizes (same array each time) and keeps doing digests looking for it to stabilize then it hits it's limit and aborts.

A fix would be to use $filter() in controller to randomize once before passing new filtered array to view and remove the random filter within the view

.controller('Controlleur', function($scope, $filter) {
    $scope.contacts = [
        {"name": "donatello", "tel": 12456},
        {"name": "michaelangelo", "tel": 12456},
        {"name": "leonardo", "tel": 12456},
        {"name": "raphael", "tel": 12456},
        {"name": "robert", "tel": 12456},
    ];

    $scope.randomContacts = $filter('random')($scope.contacts);

});

View:

<li ng-repeat="contact in randomContacts track by contact.name ">
     <strong>{{contact.name}}</strong>: {{contact.tel}}
</li>

DEMO

Upvotes: 2

Related Questions