diff --git a/src/ng/directive/ngOptions.js b/src/ng/directive/ngOptions.js
index 476da491d755..2d51cc74f4fe 100644
--- a/src/ng/directive/ngOptions.js
+++ b/src/ng/directive/ngOptions.js
@@ -32,8 +32,9 @@ var ngOptionsMinErr = minErr('ngOptions');
* option. See example below for demonstration.
*
*
- * **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.
*
*
* ## `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);
+ }
// ------------------------------------------------------------------ //
diff --git a/src/ng/directive/select.js b/src/ng/directive/select.js
index b0b69aca1f83..6533fe8906c5 100644
--- a/src/ng/directive/select.js
+++ b/src/ng/directive/select.js
@@ -135,8 +135,9 @@ var SelectController =
* option. See example below for demonstration.
*
*
- * **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.
*
*
*/
diff --git a/test/ng/directive/ngOptionsSpec.js b/test/ng/directive/ngOptionsSpec.js
index ceb3e312f494..b6187a3d3030 100644
--- a/test/ng/directive/ngOptionsSpec.js
+++ b/test/ng/directive/ngOptionsSpec.js
@@ -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();
+ });
+
});
@@ -1657,7 +1701,7 @@ describe('ngOptions', function() {
scope.values.pop();
});
- expect(element.val()).toEqual('');
+ expect(element.val()).toEqualUnknownValue();
expect(scope.selected).toEqual(null);
// Check after model change
@@ -1671,7 +1715,7 @@ describe('ngOptions', function() {
scope.values.pop();
});
- expect(element.val()).toEqual('');
+ expect(element.val()).toEqualUnknownValue();
expect(scope.selected).toEqual(null);
});