Angularjs Infinite $digest Loop When No Scope Changes
Solution 1:
I assume _.filter
returns a new array instance everytime it is run. This causes angular's implicit $watch
es like:
ng-hide="getDrawsWithResults(selectedLottery.draws)"
and
ng-repeat="draw in getDrawsWithResults(selectedLottery.draws)"
to think that the model has changed so it needs to digest again.
I would implement a filter
app.filter('withResults', function() {
return function(draws) {
return _.filter(draws, function(draw){
return draw.results && draw.results.length > 0;
});
}
})
and apply it like that (see EDIT below):
ng-hide="selectedLottery.draws | withResults"
and
ng-repeat="draw in selectedLottery.draws | withresults"
EDITED after discussion in comments
The actual problem is this binding:
ng-hide="getDrawsWithResults(selectedLottery.draws)"
ng-hide
registers a watch which will fire forever since the reference of the filtered array always changes. It can be changed to:
ng-hide="getDrawsWithResults(selectedLottery.draws).length > 0"
and the corresponding filter:
ng-hide="(selectedLottery.draws | withResults).length > 0"
ng-repeat
does not have the same problem because it registers a $watchCollection
.
Solution 2:
This implies $scope.getDrawsWithResults()
is not idempotent. Given the same input and state it doesn't consistently return the same result. And ng-hide
requires an idempotent function (as do all function that Angular has a watch on).
In short, you may be better off using a function that returns a single boolean result instead of _.filter
which returns an array. Perhaps _.all
?
The reason idempotence matters here is because of the way Angular's $digest cycle works. Because of your ng-hide
Angular places a watch on the results of your $scope.getDrawsWithResults()
. This way it can be alerted whenever it should re-evaluate that ng-hide
. Your ng-repeat
is not affected because it's results don't need to be watched by Angular.
So every time a $digest happens (which is many times a second) $scope.getDrawsWithResults()
is called to see if it's results changed from the previous $digest cycle and thus whether it should change ng-hide
. If the result has changed Angular knows that could also mean some other function it's watching (which possibly uses a result from your function) could have changed. So it needs to re-run $digest (letting the change propagate through the system if need be).
And so $digest keeps running until the results of all functions it's watching stop changing. Or until there's been 10 $digest cycles. Angular assumes that if the system isn't stable after 10 cycles it probably will never stabilise. And so it gives up and throws the error message you got.
You can dive into this all in more depth here if you'd like: http://teropa.info/blog/2013/11/03/make-your-own-angular-part-1-scopes-and-digest.html
Post a Comment for "Angularjs Infinite $digest Loop When No Scope Changes"