0

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.

2
  • You are iterating over contacts and filtering them at the same time. This means you are changing the array over which you are iterating. If angular is looping through this array and it is changing on the go, then perhaps you run into trouble. I wouldn't use a filter in this place. Commented Jul 18, 2017 at 22:32
  • I am not changing the array. array.filter() generates a new array. The first one isn't modified. Commented Jul 19, 2017 at 5:34

2 Answers 2

2

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

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

2 Comments

Oh so angular preventively run $digest several times even if nothing has changed in the scope ?
Yes...minimum 2 times in a digest cycle to dirty check the whole scope. So anything random that is generated in view logic causes issues
0

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.

Comments

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.