diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index 1793e570f2a3..ef2b192468db 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -1709,7 +1709,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ $animate.removeClass($element, PENDING_CLASS); }; - this.$$setPending = function(validationErrorKey, promise, currentValue) { + this.$$setPending = function(validationErrorKey, promise, modelValue, viewValue) { ctrl.$pending = ctrl.$pending || {}; if (angular.isUndefined(ctrl.$pending[validationErrorKey])) { ctrl.$pending[validationErrorKey] = true; @@ -1725,19 +1725,19 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ //Special-case for (undefined|null|false|NaN) values to avoid //having to compare each of them with each other - currentValue = currentValue || ''; + viewValue = viewValue || ''; promise.then(resolve(true), resolve(false)); function resolve(bool) { return function() { var value = ctrl.$viewValue || ''; - if (ctrl.$pending && ctrl.$pending[validationErrorKey] && currentValue === value) { + if (ctrl.$pending && ctrl.$pending[validationErrorKey] && viewValue === value) { pendingCount--; delete ctrl.$pending[validationErrorKey]; ctrl.$setValidity(validationErrorKey, bool); if (pendingCount === 0) { ctrl.$$clearPending(); - ctrl.$$updateValidModelValue(value); + ctrl.$$updateValidModelValue(modelValue); ctrl.$$writeModelToScope(); } } @@ -1947,7 +1947,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ throw $ngModelMinErr("$asyncValidators", "Expected asynchronous validator to return a promise but got '{0}' instead.", result); } - ctrl.$$setPending(validator, result, modelValue); + ctrl.$$setPending(validator, result, modelValue, viewValue); }); } @@ -2133,10 +2133,10 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ viewValue = formatters[idx](viewValue); } - ctrl.$$runValidators(modelValue, viewValue); if (ctrl.$viewValue !== viewValue) { ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue; + ctrl.$$runValidators(modelValue, viewValue); ctrl.$render(); } } diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index 3c5e6d437cb1..6216e875e642 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -667,6 +667,16 @@ describe('NgModelController', function() { expect(isObject(ctrl.$pending)).toBe(false); })); + it('should not run validators in case view value is not re-rendered', function() { + ctrl.$formatters.push(function(value) { + return 'nochange'; + }); + ctrl.$validators.spyValidator = jasmine.createSpy('spyValidator'); + scope.$apply('value = "first"'); + scope.$apply('value = "second"'); + expect(ctrl.$validators.spyValidator).toHaveBeenCalledOnce(); + }); + it('should re-evaluate the form validity state once the asynchronous promise has been delivered', inject(function($compile, $rootScope, $q) { @@ -730,6 +740,31 @@ describe('NgModelController', function() { dealoc(element); })); + it('should re-evaluate the form validity state against a parsed view value', + inject(function($compile, $rootScope, $q) { + var element = $compile('
')($rootScope); + var inputElm = element.find('input'); + + var formCtrl = $rootScope.myForm; + var curiousnumberCtrl = formCtrl.curiousnumber; + var curiousnumberDefer; + curiousnumberCtrl.$asyncValidators.isCurious = function() { + curiousnumberDefer = $q.defer(); + return curiousnumberDefer.promise; + }; + + curiousnumberCtrl.$setViewValue("22"); + $rootScope.$digest(); + expect(curiousnumberCtrl.$pending.isCurious).toBe(true); + + curiousnumberDefer.resolve(); + $rootScope.$digest(); + expect(curiousnumberCtrl.$pending).toBeUndefined(); + + dealoc(element); + })); }); });