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

Commit ac4e97b

Browse files
committed
fix(ngModelController): don't run $validators if modelValue hasn't changed
This fixes issues where a parser calls $setViewValue. This is a common strategy for manipulating the $viewValue while the user is entering data into an input field. When the $viewValue was changed inside the parser, the new viewValue would be committed and used for validation. The original parser however would run last and pass the original (outdated) viewValue on to the validators, which could cause false positives, e.g. for minlength.
1 parent 9fa73cb commit ac4e97b

File tree

2 files changed

+36
-1
lines changed

2 files changed

+36
-1
lines changed

src/ng/directive/input.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2198,12 +2198,22 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
21982198
ctrl.$modelValue = ngModelGet($scope);
21992199
}
22002200
var prevModelValue = ctrl.$modelValue;
2201-
var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
22022201
ctrl.$$rawModelValue = modelValue;
2202+
2203+
//If the modelValue hasn't changed since last parsing, there is no need to
2204+
//update or validate again
2205+
//This can happen if $setViewValue is called from inside a parser
2206+
if (angular.isDefined(modelValue) && prevModelValue === modelValue) {
2207+
return;
2208+
}
2209+
2210+
var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
2211+
22032212
if (allowInvalid) {
22042213
ctrl.$modelValue = modelValue;
22052214
writeToModelIfNeeded();
22062215
}
2216+
22072217
ctrl.$$runValidators(parserValid, modelValue, viewValue, function(allValid) {
22082218
if (!allowInvalid) {
22092219
// Note: Don't check ctrl.$valid here, as we could have

test/ng/directive/inputSpec.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,6 +1066,31 @@ describe('NgModelController', function() {
10661066

10671067
dealoc(element);
10681068
}));
1069+
1070+
1071+
it('should not run $validators if the modelValue did not change', function() {
1072+
ctrl.$parsers.push(function(value) {
1073+
if (value && value.substr(-1) === 'b') {
1074+
value = 'a';
1075+
ctrl.$setViewValue(value);
1076+
ctrl.$render();
1077+
}
1078+
1079+
return value;
1080+
});
1081+
1082+
ctrl.$validators.mock = function(modelValue) {
1083+
return true;
1084+
};
1085+
1086+
spyOn(ctrl.$validators, 'mock').andCallThrough();
1087+
spyOn(ctrl, '$commitViewValue').andCallThrough();
1088+
1089+
ctrl.$setViewValue('ab');
1090+
1091+
expect(ctrl.$commitViewValue.calls.length).toEqual(2);
1092+
expect(ctrl.$validators.mock).toHaveBeenCalledOnceWith('a', 'a');
1093+
});
10691094
});
10701095
});
10711096

0 commit comments

Comments
 (0)