Skip to content
Permalink
Browse files

perf(ngOptions): only perform deep equality check on ngModel if using…

… track by

Closes #11448
Closes #11447
  • Loading branch information...
booleanbetrayal authored and petebacondarwin committed Mar 27, 2015
1 parent 73f3515 commit 171b9f7f2339ef9047b8526b2c3f36bb58d14feb
Showing with 58 additions and 7 deletions.
  1. +11 −5 src/ng/directive/ngOptions.js
  2. +3 −2 src/ng/directive/select.js
  3. +44 −0 test/ng/directive/ngOptionsSpec.js
@@ -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`**
@@ -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
@@ -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);
}
// ------------------------------------------------------------------ //


@@ -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();
}
}

}

}
@@ -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>
*
*/
@@ -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();
});

});


0 comments on commit 171b9f7

Please sign in to comment.
You can’t perform that action at this time.