diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index 6f3919dfb174..a4e09f0dc588 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -385,7 +385,9 @@ var inputType = { function isEmpty(value) { - return isUndefined(value) || value === '' || value === null || value !== value; + return isUndefined(value) || value === '' || value === null || + (value.length === 0 && isArrayLike(value)) || + value !== value; } @@ -1082,11 +1084,11 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ // model -> value var ctrl = this; - $scope.$watch(function ngModelWatch() { + $scope.$watchCollection($attr.ngModel, function ngModelWatch(newValue, oldValue) { var value = ngModelGet($scope); // if scope model value and ngModel value are out of sync - if (ctrl.$modelValue !== value) { + if (!equals(ctrl.$modelValue, oldValue)) { var formatters = ctrl.$formatters, idx = formatters.length; diff --git a/src/ng/directive/select.js b/src/ng/directive/select.js index a62f706c52da..296491bcef4f 100644 --- a/src/ng/directive/select.js +++ b/src/ng/directive/select.js @@ -266,7 +266,6 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) { } function Multiple(scope, selectElement, ctrl) { - var lastView; ctrl.$render = function() { var items = new HashMap(ctrl.$viewValue); forEach(selectElement.find('option'), function(option) { @@ -274,13 +273,10 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) { }); }; - // we have to do it on each watch since ngModel watches reference, but - // we need to work of an array, so we need to see if anything was inserted/removed - scope.$watch(function selectMultipleWatch() { - if (!equals(lastView, ctrl.$viewValue)) { - lastView = copy(ctrl.$viewValue); - ctrl.$render(); - } + scope.$watchCollection(attr.ngModel, function selectMultipleWatch(newValue, oldValue) { + ctrl.$viewValue = newValue; + requiredValidator && requiredValidator(newValue); + ctrl.$render(); }); selectElement.on('change', function() { diff --git a/src/ng/rootScope.js b/src/ng/rootScope.js index d3b2762d8c01..22e7787c9707 100644 --- a/src/ng/rootScope.js +++ b/src/ng/rootScope.js @@ -362,6 +362,7 @@ function $RootScopeProvider(){ $watchCollection: function(obj, listener) { var self = this; var oldValue; + var oldArray; var newValue; var changeDetected = 0; var objGetter = $parse(obj); @@ -371,6 +372,7 @@ function $RootScopeProvider(){ function $watchCollectionWatch() { newValue = objGetter(self); + oldArray = null; var newLength, key; if (!isObject(newValue)) { @@ -386,6 +388,8 @@ function $RootScopeProvider(){ changeDetected++; } + oldArray = oldValue.slice(0); + newLength = newValue.length; if (oldLength !== newLength) { @@ -439,7 +443,7 @@ function $RootScopeProvider(){ } function $watchCollectionAction() { - listener(newValue, oldValue, self); + listener(newValue, oldArray || oldValue, self); } return this.$watch($watchCollectionWatch, $watchCollectionAction); diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index facc2b806bc9..814c72ec9a3a 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -986,7 +986,7 @@ describe('input', function() { expect(scope.list).toEqual(['a']); changeInputValueTo('a , b'); - expect(inputElm.val()).toEqual('a , b'); + expect(inputElm.val()).toEqual('a, b'); expect(scope.list).toEqual(['a', 'b']); }); @@ -1019,6 +1019,28 @@ describe('input', function() { changeInputValueTo('a,b: c'); expect(scope.list).toEqual(['a', 'b', 'c']); }); + + it("should detect changes in the values of an array", function () { + var list = ['x', 'y', 'z']; + compileInput(''); + scope.$apply(function() { + scope.list = list; + }); + expect(inputElm.val()).toBe('x, y, z'); + scope.$apply(function() { + list.unshift('w'); + }); + expect(inputElm.val()).toBe('w, x, y, z'); + }); + + it('should be invalid if empty', function() { + compileInput(''); + changeInputValueTo('a'); + expect(inputElm).toBeValid(); + changeInputValueTo(''); + expect(inputElm).toBeInvalid(); + }); + }); describe('required', function() { diff --git a/test/ng/rootScopeSpec.js b/test/ng/rootScopeSpec.js index 656385e9681c..83b49bead4c1 100644 --- a/test/ng/rootScopeSpec.js +++ b/test/ng/rootScopeSpec.js @@ -510,6 +510,28 @@ describe('Scope', function() { $rootScope.$digest(); expect(arrayLikelog).toEqual(['x', 'y']); }); + + it('should return a new array with old values', function(){ + var watchArgs; + $rootScope.$watchCollection('obj', function (newValues, oldValues) { + watchArgs = { + newValues: newValues, + oldValues: oldValues + }; + }); + + $rootScope.obj = ['a']; + $rootScope.$digest(); + + expect(watchArgs.newValues).toEqual($rootScope.obj); + expect(watchArgs.oldValues).toEqual([]); + + $rootScope.obj.push('b'); + $rootScope.$digest(); + + expect(watchArgs.newValues).toEqual(['a', 'b']); + expect(watchArgs.oldValues).toEqual(['a']); + }) });