diff --git a/src/ng/directive/ngModel.js b/src/ng/directive/ngModel.js index 1db2283586c4..7257b5a08dd6 100644 --- a/src/ng/directive/ngModel.js +++ b/src/ng/directive/ngModel.js @@ -233,6 +233,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ this.$dirty = false; this.$valid = true; this.$invalid = false; + this.$$parseError = false; this.$error = {}; // keep invalid keys here this.$$success = {}; // keep valid keys here this.$pending = undefined; // keep pending keys here @@ -518,7 +519,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ // Check if the there's a parse error, so we don't unset it accidentially var parserName = ctrl.$$parserName || 'parse'; - var parserValid = ctrl.$error[parserName] ? false : undefined; + var parserValid = ctrl.$$parseError ? false : undefined; var prevValid = ctrl.$valid; var prevModelValue = ctrl.$modelValue; @@ -563,7 +564,6 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ if (parseValid === undefined) { setValidity(errorKey, null); } else { - setValidity(errorKey, parseValid); if (!parseValid) { forEach(ctrl.$validators, function(v, name) { setValidity(name, null); @@ -571,9 +571,14 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ forEach(ctrl.$asyncValidators, function(v, name) { setValidity(name, null); }); - return false; + ctrl.$$parseError = true; } + // Set the parse error last, to prevent unsetting it, should a $validators key == parserName + setValidity(errorKey, parseValid); + return parseValid; } + + ctrl.$$parseError = false; return true; } @@ -814,6 +819,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ // TODO(perf): why not move this to the action fn? if (modelValue !== ctrl.$modelValue) { ctrl.$modelValue = ctrl.$$rawModelValue = modelValue; + ctrl.$$parseError = false; var formatters = ctrl.$formatters, idx = formatters.length; diff --git a/test/ng/directive/ngModelSpec.js b/test/ng/directive/ngModelSpec.js index 583414deaa9c..b7857ad3dc0a 100644 --- a/test/ng/directive/ngModelSpec.js +++ b/test/ng/directive/ngModelSpec.js @@ -1221,6 +1221,96 @@ describe('ngModel', function() { expect(ctrl.$validators.mock).toHaveBeenCalledWith('a', 'ab'); expect(ctrl.$validators.mock.calls.length).toEqual(2); }); + + iit('should validate correctly when $parser name equals $validator key', function() { + + ctrl.$validators.parserOrValidator = function(value) { + switch (value) { + case 'allInvalid': + case 'parseValid-validatorsInvalid': + case 'stillParseValid-validatorsInvalid': + return false; + default: + return true; + } + }; + + ctrl.$validators.validator = function(value) { + switch (value) { + case 'allInvalid': + case 'parseValid-validatorsInvalid': + case 'stillParseValid-validatorsInvalid': + return false; + default: + return true; + } + }; + + ctrl.$$parserName = 'parserOrValidator'; + ctrl.$parsers.push(function(value) { + switch (value) { + case 'allInvalid': + case 'stillAllInvalid': + case 'parseInvalid-validatorsValid': + case 'stillParseInvalid-validatorsValid': + return undefined; + default: + return value; + } + }); + + //Parser and validators are invalid + scope.$apply('value = "allInvalid"'); + expect(scope.value).toBe('allInvalid'); + expect(ctrl.$error).toEqual({parserOrValidator: true, validator: true}); + + ctrl.$validate(); + expect(scope.value).toEqual('allInvalid'); + expect(ctrl.$error).toEqual({parserOrValidator: true, validator: true}); + + ctrl.$setViewValue('stillAllInvalid'); + expect(scope.value).toBeUndefined(); + expect(ctrl.$error).toEqual({parserOrValidator: true}); + + ctrl.$validate(); + expect(scope.value).toBeUndefined(); + expect(ctrl.$error).toEqual({parserOrValidator: true}); + + //Parser is valid, validators are invalid + scope.$apply('value = "parseValid-validatorsInvalid"'); + expect(scope.value).toBe('parseValid-validatorsInvalid'); + expect(ctrl.$error).toEqual({parserOrValidator: true, validator: true}); + + ctrl.$validate(); + expect(scope.value).toBe('parseValid-validatorsInvalid'); + expect(ctrl.$error).toEqual({parserOrValidator: true, validator: true}); + + ctrl.$setViewValue('stillParseValid-validatorsInvalid'); + expect(scope.value).toBeUndefined(); + expect(ctrl.$error).toEqual({parserOrValidator: true, validator: true}); + + ctrl.$validate(); + expect(scope.value).toBeUndefined(); + expect(ctrl.$error).toEqual({parserOrValidator: true, validator: true}); + + //Parser is invalid, validators are valid + scope.$apply('value = "parseInvalid-validatorsValid"'); + expect(scope.value).toBe('parseInvalid-validatorsValid'); + expect(ctrl.$error).toEqual({}); + + ctrl.$validate(); + expect(scope.value).toBe('parseInvalid-validatorsValid'); + expect(ctrl.$error).toEqual({}); + + ctrl.$setViewValue('stillParseInvalid-validatorsValid'); + expect(scope.value).toBeUndefined(); + expect(ctrl.$error).toEqual({parserOrValidator: true}); + + ctrl.$validate(); + expect(scope.value).toBeUndefined(); + expect(ctrl.$error).toEqual({parserOrValidator: true}); + }); + }); });