Passing in an a function to the ngOptions array clause that generates elements on the fly results in infinite $digest loop #9464
Description
AngularJS 1.2.22 (in particular, commit c286094) has changed the behaviour of ngOptions from deep watching to shallow watching the elements of the array. If the array and its elements are generated by a function on the fly, shallow watching will fail as even if the elements are identical, they are separate instances. This results in infinite $digest loops:
Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: [["fn: $watchCollectionWatch; newVal: 37; oldVal: 32"],["fn: $watchCollectionWatch; newVal: 42; oldVal: 37"],["fn: $watchCollectionWatch; newVal: 47; oldVal: 42"],["fn: $watchCollectionWatch; newVal: 52; oldVal: 47"],["fn: $watchCollectionWatch; newVal: 57; oldVal: 52"]]
http://errors.angularjs.org/"NG_VERSION_FULL"/$rootScope/infdig?p0=10&p1=%5B%5B%22fn%3A%20%24watchCollectionWatch%3B%20newVal%3A%2037%3B%20oldVal%3A%2032%22%5D%2C%5B%22fn%3A%20%24watchCollectionWatch%3B%20newVal%3A%2042%3B%20oldVal%3A%2037%22%5D%2C%5B%22fn%3A%20%24watchCollectionWatch%3B%20newVal%3A%2047%3B%20oldVal%3A%2042%22%5D%2C%5B%22fn%3A%20%24watchCollectionWatch%3B%20newVal%3A%2052%3B%20oldVal%3A%2047%22%5D%2C%5B%22fn%3A%20%24watchCollectionWatch%3B%20newVal%3A%2057%3B%20oldVal%3A%2052%22%5D%5D
at /Users/tamas/build/github.com/drjokepu/angular.js/src/minErr.js:73:12
at Scope.$digest (/Users/tamas/build/github.com/drjokepu/angular.js/src/ng/rootScope.js:705:19)
at Scope.$apply (/Users/tamas/build/github.com/drjokepu/angular.js/src/ng/rootScope.js:931:24)
at null.<anonymous> (/Users/tamas/build/github.com/drjokepu/angular.js/test/ng/directive/selectSpec.js:1142:15)
A tracking expression clause is supported by ngOptions that explicitly defines the expression that is used for comparing array elements. However, currently this is only used for identifying the selected item and not used for watching the elements of the array itself. I am suggesting extending the usage of this expression to be also used for differentiating of the array items themselves when shallow watching.
Reproducible: always
Browsers: Tested on Chrome 37, Safari 7.1
Operating System: OS X 10.9.5
Steps to reproduce:
- Create a new html file and add the following content:
<!DOCTYPE html>
<html>
<head>
<title>Infinite Digest</title>
</head>
<body>
<body ng-app="demoApp">
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
<script type='text/javascript'>
angular.module('demoApp', []).controller('DemoController', function($scope) {
$scope.makeOptions = function() {
var options = [];
for (var i = 0; i < 5; i++) {
options.push({ label: 'Value = ' + i, value: i });
}
return options;
};
$scope.selected = { label: 'Value = 1', value: 1 };
});
</script>
<div ng-controller="DemoController">
<div>
<h2>Infinite Digest</h2>
<select ng-model="selected"
ng-options="opt as opt.label for opt in makeOptions() track by opt.value">
</select>
The value selected is {{ selected.value }}.
</div>
</div>
</body>
</body>
</html>
- Open this file in a browser. AngularJS will report infinite digest in the console.
This works fine on AngularJS 1.2.21 and earlier.
I am not really an Angular developer (or a real JavaScript developer for that matter), but I am going to submit a pull request with a spec test as well as a proposed solution that will illustrate how I would go about tackling the issue,