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

Commit 171b9f7

Browse files
booleanbetrayalpetebacondarwin
authored andcommitted
perf(ngOptions): only perform deep equality check on ngModel if using track by
Closes #11448 Closes #11447
1 parent 73f3515 commit 171b9f7

File tree

3 files changed

+58
-7
lines changed

3 files changed

+58
-7
lines changed

src/ng/directive/ngOptions.js

+11-5
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ var ngOptionsMinErr = minErr('ngOptions');
3232
* option. See example below for demonstration.
3333
*
3434
* <div class="alert alert-warning">
35-
* **Note:** `ngModel` compares by reference, not value. This is important when binding to an
36-
* array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/).
35+
* **Note:** By default, `ngModel` compares by reference, not value. This is important when binding to an
36+
* array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/). When using `track by`
37+
* in an `ngOptions` expression, however, deep equality checks will be performed.
3738
* </div>
3839
*
3940
* ## `select` **`as`**
@@ -275,6 +276,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
275276
}
276277

277278
return {
279+
trackBy: trackBy,
278280
getWatchables: $parse(valuesFn, function(values) {
279281
// Create a collection of things that we would like to watch (watchedArray)
280282
// so that they can all be watched using a single $watchCollection
@@ -500,8 +502,9 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
500502

501503
// We also need to watch to see if the internals of the model changes, since
502504
// ngModel only watches for object identity change
503-
scope.$watch(attr.ngModel, function() { ngModelCtrl.$render(); }, true);
504-
505+
if (ngOptions.trackBy) {
506+
scope.$watch(attr.ngModel, function() { ngModelCtrl.$render(); }, true);
507+
}
505508
// ------------------------------------------------------------------ //
506509

507510

@@ -643,10 +646,13 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
643646
// Check to see if the value has changed due to the update to the options
644647
if (!ngModelCtrl.$isEmpty(previousValue)) {
645648
var nextValue = selectCtrl.readValue();
646-
if (!equals(previousValue, nextValue)) {
649+
if (ngOptions.trackBy && !equals(previousValue, nextValue) ||
650+
previousValue !== nextValue) {
647651
ngModelCtrl.$setViewValue(nextValue);
652+
ngModelCtrl.$render();
648653
}
649654
}
655+
650656
}
651657

652658
}

src/ng/directive/select.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,9 @@ var SelectController =
135135
* option. See example below for demonstration.
136136
*
137137
* <div class="alert alert-warning">
138-
* **Note:** `ngModel` compares by reference, not value. This is important when binding to an
139-
* array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/).
138+
* **Note:** By default, `ngModel` compares by reference, not value. This is important when binding to an
139+
* array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/). When using `track by`
140+
* in an `ngOptions` expression, however, deep equality checks will be performed.
140141
* </div>
141142
*
142143
*/

test/ng/directive/ngOptionsSpec.js

+44
Original file line numberDiff line numberDiff line change
@@ -1041,6 +1041,50 @@ describe('ngOptions', function() {
10411041
});
10421042
}).not.toThrow();
10431043
});
1044+
1045+
it('should setup equality watches on ngModel changes if using trackBy', function() {
1046+
1047+
createSelect({
1048+
'ng-model': 'selected',
1049+
'ng-options': 'item for item in arr track by item.id'
1050+
});
1051+
1052+
scope.$apply(function() {
1053+
scope.selected = scope.arr[0];
1054+
});
1055+
1056+
spyOn(element.controller('ngModel'), '$render');
1057+
1058+
scope.$apply(function() {
1059+
scope.selected.label = 'changed';
1060+
});
1061+
1062+
// update render due to equality watch
1063+
expect(element.controller('ngModel').$render).toHaveBeenCalled();
1064+
1065+
});
1066+
1067+
it('should not setup equality watches on ngModel changes if not using trackBy', function() {
1068+
1069+
createSelect({
1070+
'ng-model': 'selected',
1071+
'ng-options': 'item for item in arr'
1072+
});
1073+
1074+
scope.$apply(function() {
1075+
scope.selected = scope.arr[0];
1076+
});
1077+
1078+
spyOn(element.controller('ngModel'), '$render');
1079+
1080+
scope.$apply(function() {
1081+
scope.selected.label = 'changed';
1082+
});
1083+
1084+
// no render update as no equality watch
1085+
expect(element.controller('ngModel').$render).not.toHaveBeenCalled();
1086+
});
1087+
10441088
});
10451089

10461090

0 commit comments

Comments
 (0)