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

Commit 274e935

Browse files
committed
fix(ngModel): validate pattern against the viewValue
Since the HTML5 pattern validation constraint validates the input value, we should also validate against the viewValue. While this worked in core up to Angular 1.2, in 1.3, we changed not only validation, but the way `input[date]` and `input[number]` are handled - they parse their input values into `Date` and `Number` respectively, which cannot be validated by a regex. Fixes #12344 BREAKING CHANGE: The `ngPattern` and `pattern` directives will validate the regex against the `viewValue` of `ngModel`, i.e. the value of the model before the $parsers are applied. Previously, the modelValue (the result of the $parsers) was validated. This fixes issues where `input[date]` and `input[number]` cannot be validated because the viewValue string is parsed into `Date` and `Number` respectively (starting with Angular 1.3). It also brings the directives in line with HTML5 constraint validation, which validates against the input value. This change is unlikely to cause applications to fail, because even in Angular 1.2, the value that was validated by pattern could have been manipulated by the $parsers, as all validation was done inside this pipeline. If you rely on the pattern being validated against the modelValue, you must create your own validator directive that overwrites the built-in pattern validator: ``` .directive('patternModelOverwrite', function patternModelOverwriteDirective() { return { restrict: 'A', require: '?ngModel', priority: 1, compile: function() { var regexp, patternExp; return { pre: function(scope, elm, attr, ctrl) { if (!ctrl) return; attr.$observe('pattern', function(regex) { /** * The built-in directive will call our overwritten validator * (see below). We just need to update the regex. * The preLink fn guaranetees our observer is called first. */ if (isString(regex) && regex.length > 0) { regex = new RegExp('^' + regex + '$'); } if (regex && !regex.test) { //The built-in validator will throw at this point return; } regexp = regex || undefined; }); }, post: function(scope, elm, attr, ctrl) { if (!ctrl) return; regexp, patternExp = attr.ngPattern || attr.pattern; //The postLink fn guarantees we overwrite the built-in pattern validator ctrl.$validators.pattern = function(value) { return ctrl.$isEmpty(value) || isUndefined(regexp) || regexp.test(value); }; } }; } }; }); ```
1 parent f7622dc commit 274e935

File tree

2 files changed

+18
-2
lines changed

2 files changed

+18
-2
lines changed

src/ng/directive/validators.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@ var patternDirective = function() {
4343
ctrl.$validate();
4444
});
4545

46-
ctrl.$validators.pattern = function(value) {
47-
return ctrl.$isEmpty(value) || isUndefined(regexp) || regexp.test(value);
46+
ctrl.$validators.pattern = function(modelValue, viewValue) {
47+
// HTML5 pattern constraint validates the input value, so we validate the viewValue
48+
return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
4849
};
4950
}
5051
};

test/ng/directive/validatorsSpec.js

+15
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,21 @@ describe('validators', function() {
204204
expect($rootScope.form.test.$error.pattern).toBe(true);
205205
expect(inputElm).not.toBeValid();
206206
});
207+
208+
209+
it('should validate the viewValue and not the modelValue', function() {
210+
var inputElm = helper.compileInput('<input type="text" name="test" ng-model="value" pattern="\\d{4}">');
211+
var ctrl = inputElm.controller('ngModel');
212+
213+
ctrl.$parsers.push(function(value) {
214+
return (value * 10) + '';
215+
});
216+
217+
helper.changeInputValueTo('1234');
218+
expect($rootScope.form.test.$error.pattern).not.toBe(true);
219+
expect($rootScope.form.test.$modelValue).toBe('12340');
220+
expect(inputElm).toBeValid();
221+
});
207222
});
208223

209224

0 commit comments

Comments
 (0)