Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit fc21db8

Browse files
fix(ngOptions): prevent infinite digest if track by expression is stable
Closes #9464
1 parent b4bdec3 commit fc21db8

File tree

2 files changed

+28
-5
lines changed

2 files changed

+28
-5
lines changed

src/ng/directive/ngOptions.js

+11-5
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,14 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
227227
var valueFn = $parse(match[2] ? match[1] : valueName);
228228
var selectAsFn = selectAs && $parse(selectAs);
229229
var viewValueFn = selectAsFn || valueFn;
230-
var trackByFn = trackBy ? $parse(trackBy) :
231-
function getHashOfValue(value) { return hashKey(value); };
230+
var trackByFn = trackBy && $parse(trackBy);
231+
232+
// Get the value by which we are going to track the option
233+
// if we have a trackFn then use that (passing scope and locals)
234+
// otherwise just hash the given viewValue
235+
var getTrackByValue = trackBy ?
236+
function(viewValue, locals) { return trackByFn(scope, locals); } :
237+
function getHashOfValue(viewValue) { return hashKey(viewValue); };
232238
var displayFn = $parse(match[2] || match[1]);
233239
var groupByFn = $parse(match[3] || '');
234240
var valuesFn = $parse(match[7]);
@@ -263,7 +269,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
263269
Object.keys(values).forEach(function getWatchable(key) {
264270
var locals = getLocals(values[key], key);
265271
var label = displayFn(scope, locals);
266-
var selectValue = viewValueFn(scope, locals);
272+
var selectValue = getTrackByValue(values[key], locals);
267273
watchedArray.push(selectValue);
268274
watchedArray.push(label);
269275
});
@@ -288,7 +294,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
288294
var value = optionValues[key];
289295
var locals = getLocals(value, key);
290296
var viewValue = viewValueFn(scope, locals);
291-
var selectValue = trackByFn(viewValue, locals);
297+
var selectValue = getTrackByValue(viewValue, locals);
292298
var label = displayFn(scope, locals);
293299
var group = groupByFn(scope, locals);
294300
var optionItem = new Option(selectValue, viewValue, label, group);
@@ -301,7 +307,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
301307
items: optionItems,
302308
selectValueMap: selectValueMap,
303309
getOptionFromViewValue: function(value) {
304-
return selectValueMap[trackByFn(value, getLocals(value))];
310+
return selectValueMap[getTrackByValue(value, getLocals(value))];
305311
}
306312
};
307313
}

test/ng/directive/ngOptionsSpec.js

+17
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,23 @@ describe('ngOptions', function() {
706706
browserTrigger(element, 'change');
707707
expect(scope.selected).toEqual([scope.obj['1'], scope.obj['2']]);
708708
});
709+
710+
it('should prevent infinite digest if track by expression is stable', function() {
711+
scope.makeOptions = function() {
712+
var options = [];
713+
for (var i = 0; i < 5; i++) {
714+
options.push({ label: 'Value = ' + i, value: i });
715+
}
716+
return options;
717+
};
718+
scope.selected = { label: 'Value = 1', value: 1 };
719+
expect(function() {
720+
createSelect({
721+
'ng-model': 'selected',
722+
'ng-options': 'item.label for item in makeOptions() track by item.value'
723+
});
724+
}).not.toThrow();
725+
});
709726
});
710727

711728

0 commit comments

Comments
 (0)