Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

fix(ngModel): fix issues when parserName is same as validator key #10850

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions src/ng/directive/ngModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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';
Copy link
Contributor

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'?

Copy link
Contributor

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

var parserValid = ctrl.$error[parserName] ? false : undefined;
var parserValid = ctrl.$$parseError ? false : undefined;

var prevValid = ctrl.$valid;
var prevModelValue = ctrl.$modelValue;
Expand Down Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The 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 parserName variable then this makes less sense.

setValidity(errorKey, parseValid);
return parseValid;
}

ctrl.$$parseError = false;
return true;
}

Expand Down Expand Up @@ -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;
Expand Down
90 changes: 90 additions & 0 deletions test/ng/directive/ngModelSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iit


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});
});

Copy link
Contributor

Choose a reason for hiding this comment

The 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:

  • parser -> isEven(value)
  • test -> isGreaterThanTen(value)
  • other -> isOdd(value)

Then we don't have to grok as many variables - just simply send in the following values:

  • 1
  • 2
  • 11
  • 12

And check that we get the correct validation?

});
});

Expand Down