-
Notifications
You must be signed in to change notification settings - Fork 27.4k
fix(ngModel): fix issues when parserName is same as validator key #10850
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,17 +564,21 @@ 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); | ||
}); | ||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment doesn't read very well. Also if we remove the unused |
||
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; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1221,6 +1221,80 @@ describe('ngModel', function() { | |
expect(ctrl.$validators.mock).toHaveBeenCalledWith('a', 'ab'); | ||
expect(ctrl.$validators.mock.calls.length).toEqual(2); | ||
}); | ||
|
||
it('should validate correctly when $parser name equals $validator key', function() { | ||
|
||
var testValid, otherValid, parseValid; | ||
|
||
ctrl.$validators.test = function(value) { | ||
return testValid; | ||
}; | ||
|
||
ctrl.$validators.other = function(value) { | ||
return otherValid; | ||
}; | ||
|
||
ctrl.$$parserName = 'test'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any ideas? It's currently a private API, but we rely on it for the name of the error key. A separate function, $setParseError(name) might be possible that is called inside the parse error. Although I think we have to stick with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could also allow returning an object that contains parser name, error state, and return value. This would allow returning undefined as a valid value, as well as get rid of $$parserName and expose an API to actually set the parser name. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about we use the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just noticed that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, how about we do something like: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's all IEs, up to IE11, so I don't think it's an option. |
||
ctrl.$parsers.push(function(value) { | ||
return parseValid ? true : undefined; | ||
}); | ||
|
||
|
||
testValid = otherValid = parseValid = false; | ||
scope.$apply('value = "allInvalid"'); | ||
expect(scope.value).toBe('allInvalid'); | ||
expect(ctrl.$error).toEqual({test: true, other: true}); | ||
|
||
ctrl.$validate(); | ||
expect(scope.value).toBe('allInvalid'); | ||
expect(ctrl.$error).toEqual({test: true, other: true}); | ||
|
||
ctrl.$setViewValue('stillAllInvalid'); | ||
expect(scope.value).toBeUndefined(); | ||
expect(ctrl.$error).toEqual({test: true}); | ||
|
||
ctrl.$validate(); | ||
expect(scope.value).toBeUndefined(); | ||
expect(ctrl.$error).toEqual({test: true}); | ||
|
||
|
||
parseValid = true; | ||
scope.$apply('value = "parseValidAndValidatorInvalid"'); | ||
expect(scope.value).toBe('parseValidAndValidatorInvalid'); | ||
expect(ctrl.$error).toEqual({test: true, other: true}); | ||
|
||
ctrl.$validate(); | ||
expect(scope.value).toBe('parseValidAndValidatorInvalid'); | ||
expect(ctrl.$error).toEqual({test: true, other: true}); | ||
|
||
ctrl.$setViewValue('stillParseValidAndValidatorInvalid'); | ||
expect(scope.value).toBeUndefined(); | ||
expect(ctrl.$error).toEqual({test: true, other: true}); | ||
|
||
ctrl.$validate(); | ||
expect(scope.value).toBeUndefined(); | ||
expect(ctrl.$error).toEqual({test: true, other: true}); | ||
|
||
|
||
parseValid = false; | ||
testValid = otherValid = true; | ||
scope.$apply('value = "parseInvalidAndValidatorValid"'); | ||
expect(scope.value).toBe('parseInvalidAndValidatorValid'); | ||
expect(ctrl.$error).toEqual({}); | ||
|
||
ctrl.$validate(); | ||
expect(scope.value).toBe('parseInvalidAndValidatorValid'); | ||
expect(ctrl.$error).toEqual({}); | ||
|
||
ctrl.$setViewValue('stillParseInvalidAndValidatorValid'); | ||
expect(scope.value).toBeUndefined(); | ||
expect(ctrl.$error).toEqual({test: true}); | ||
|
||
ctrl.$validate(); | ||
expect(scope.value).toBeUndefined(); | ||
expect(ctrl.$error).toEqual({test: true}); | ||
}); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test seems overly complicated. Couldn't we replace the parser and validators with simple functions, like:
Then we don't have to grok as many variables - just simply send in the following values:
And check that we get the correct validation? |
||
}); | ||
}); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This variable doesn't seem to be used now. What does this mean for the default of 'parse'?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see that the
errorKey
variable sets the default below on line 563