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

Commit

Permalink
perf(ngOptions): only perform deep equality check on ngModel if using…
Browse files Browse the repository at this point in the history
… track by

Closes #11448
Closes #11447
  • Loading branch information
booleanbetrayal authored and petebacondarwin committed Mar 30, 2015
1 parent 73f3515 commit 171b9f7
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 7 deletions.
16 changes: 11 additions & 5 deletions src/ng/directive/ngOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ var ngOptionsMinErr = minErr('ngOptions');
* option. See example below for demonstration.
*
* <div class="alert alert-warning">
* **Note:** `ngModel` compares by reference, not value. This is important when binding to an
* array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/).
* **Note:** By default, `ngModel` compares by reference, not value. This is important when binding to an
* array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/). When using `track by`
* in an `ngOptions` expression, however, deep equality checks will be performed.
* </div>
*
* ## `select` **`as`**
Expand Down Expand Up @@ -275,6 +276,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
}

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

// We also need to watch to see if the internals of the model changes, since
// ngModel only watches for object identity change
scope.$watch(attr.ngModel, function() { ngModelCtrl.$render(); }, true);

if (ngOptions.trackBy) {
scope.$watch(attr.ngModel, function() { ngModelCtrl.$render(); }, true);
}
// ------------------------------------------------------------------ //


Expand Down Expand Up @@ -643,10 +646,13 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
// Check to see if the value has changed due to the update to the options
if (!ngModelCtrl.$isEmpty(previousValue)) {
var nextValue = selectCtrl.readValue();
if (!equals(previousValue, nextValue)) {
if (ngOptions.trackBy && !equals(previousValue, nextValue) ||
previousValue !== nextValue) {
ngModelCtrl.$setViewValue(nextValue);
ngModelCtrl.$render();
}
}

}

}
Expand Down
5 changes: 3 additions & 2 deletions src/ng/directive/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,9 @@ var SelectController =
* option. See example below for demonstration.
*
* <div class="alert alert-warning">
* **Note:** `ngModel` compares by reference, not value. This is important when binding to an
* array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/).
* **Note:** By default, `ngModel` compares by reference, not value. This is important when binding to an
* array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/). When using `track by`
* in an `ngOptions` expression, however, deep equality checks will be performed.
* </div>
*
*/
Expand Down
44 changes: 44 additions & 0 deletions test/ng/directive/ngOptionsSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1041,6 +1041,50 @@ describe('ngOptions', function() {
});
}).not.toThrow();
});

it('should setup equality watches on ngModel changes if using trackBy', function() {

createSelect({
'ng-model': 'selected',
'ng-options': 'item for item in arr track by item.id'
});

scope.$apply(function() {
scope.selected = scope.arr[0];
});

spyOn(element.controller('ngModel'), '$render');

scope.$apply(function() {
scope.selected.label = 'changed';
});

// update render due to equality watch
expect(element.controller('ngModel').$render).toHaveBeenCalled();

});

it('should not setup equality watches on ngModel changes if not using trackBy', function() {

createSelect({
'ng-model': 'selected',
'ng-options': 'item for item in arr'
});

scope.$apply(function() {
scope.selected = scope.arr[0];
});

spyOn(element.controller('ngModel'), '$render');

scope.$apply(function() {
scope.selected.label = 'changed';
});

// no render update as no equality watch
expect(element.controller('ngModel').$render).not.toHaveBeenCalled();
});

});


Expand Down

0 comments on commit 171b9f7

Please sign in to comment.