From bf60418fdc2d6efdd8332242530e0803a07a3334 Mon Sep 17 00:00:00 2001 From: Dzmitry Shylovich Date: Wed, 19 Oct 2016 19:37:54 +0300 Subject: [PATCH] feat(forms): Validator.pattern accepts a RegExp (#12323) --- modules/@angular/forms/src/validators.ts | 25 ++++++++++------ .../@angular/forms/test/validators_spec.ts | 30 +++++++++++++++---- tools/public_api_guard/forms/index.d.ts | 2 +- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/modules/@angular/forms/src/validators.ts b/modules/@angular/forms/src/validators.ts index 46fdd09efd..5e099e3a8b 100644 --- a/modules/@angular/forms/src/validators.ts +++ b/modules/@angular/forms/src/validators.ts @@ -8,7 +8,6 @@ import {OpaqueToken} from '@angular/core'; import {toPromise} from 'rxjs/operator/toPromise'; - import {AsyncValidatorFn, ValidatorFn} from './directives/validators'; import {StringMapWrapper} from './facade/collection'; import {isPresent} from './facade/lang'; @@ -95,16 +94,24 @@ export class Validators { /** * Validator that requires a control to match a regex to its value. */ - static pattern(pattern: string): ValidatorFn { + static pattern(pattern: string|RegExp): ValidatorFn { + if (!pattern) return Validators.nullValidator; + let regex: RegExp; + let regexStr: string; + if (typeof pattern === 'string') { + regexStr = `^${pattern}$`; + regex = new RegExp(regexStr); + } else { + regexStr = pattern.toString(); + regex = pattern; + } return (control: AbstractControl): {[key: string]: any} => { if (isEmptyInputValue(control.value)) { return null; // don't validate empty values to allow optional controls } - const regex = new RegExp(`^${pattern}$`); const value: string = control.value; - return regex.test(value) ? - null : - {'pattern': {'requiredPattern': `^${pattern}$`, 'actualValue': value}}; + return regex.test(value) ? null : + {'pattern': {'requiredPattern': regexStr, 'actualValue': value}}; }; } @@ -119,7 +126,7 @@ export class Validators { */ static compose(validators: ValidatorFn[]): ValidatorFn { if (!validators) return null; - var presentValidators = validators.filter(isPresent); + const presentValidators = validators.filter(isPresent); if (presentValidators.length == 0) return null; return function(control: AbstractControl) { @@ -129,7 +136,7 @@ export class Validators { static composeAsync(validators: AsyncValidatorFn[]): AsyncValidatorFn { if (!validators) return null; - var presentValidators = validators.filter(isPresent); + const presentValidators = validators.filter(isPresent); if (presentValidators.length == 0) return null; return function(control: AbstractControl) { @@ -152,7 +159,7 @@ function _executeAsyncValidators(control: AbstractControl, validators: AsyncVali } function _mergeErrors(arrayOfErrors: any[]): {[key: string]: any} { - var res: {[key: string]: any} = + const res: {[key: string]: any} = arrayOfErrors.reduce((res: {[key: string]: any}, errors: {[key: string]: any}) => { return isPresent(errors) ? StringMapWrapper.merge(res, errors) : res; }, {}); diff --git a/modules/@angular/forms/test/validators_spec.ts b/modules/@angular/forms/test/validators_spec.ts index abfbdc28ae..945eed04e3 100644 --- a/modules/@angular/forms/test/validators_spec.ts +++ b/modules/@angular/forms/test/validators_spec.ts @@ -17,7 +17,7 @@ import {EventEmitter} from '../src/facade/async'; export function main() { function validator(key: string, error: any) { return function(c: AbstractControl) { - var r: {[k: string]: string} = {}; + const r: {[k: string]: string} = {}; r[key] = error; return r; }; @@ -101,13 +101,31 @@ export function main() { () => { expect(Validators.pattern('null')(new FormControl(null))).toBeNull(); }); it('should not error on valid strings', - () => { expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaAA'))).toBeNull(); }); + () => expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaAA'))).toBeNull()); it('should error on failure to match string', () => { expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaa0'))).toEqual({ 'pattern': {'requiredPattern': '^[a-zA-Z ]*$', 'actualValue': 'aaa0'} }); }); + + it('should accept RegExp object', () => { + const pattern: RegExp = new RegExp('[a-zA-Z ]+'); + expect(Validators.pattern(pattern)(new FormControl('aaAA'))).toBeNull(); + }); + + it('should error on failure to match RegExp object', () => { + const pattern: RegExp = new RegExp('^[a-zA-Z ]*$'); + expect(Validators.pattern(pattern)(new FormControl('aaa0'))).toEqual({ + 'pattern': {'requiredPattern': '/^[a-zA-Z ]*$/', 'actualValue': 'aaa0'} + }); + }); + + it('should not error on "null" pattern', + () => expect(Validators.pattern(null)(new FormControl('aaAA'))).toBeNull()); + + it('should not error on "undefined" pattern', + () => expect(Validators.pattern(undefined)(new FormControl('aaAA'))).toBeNull()); }); describe('compose', () => { @@ -115,22 +133,22 @@ export function main() { () => { expect(Validators.compose(null)).toBe(null); }); it('should collect errors from all the validators', () => { - var c = Validators.compose([validator('a', true), validator('b', true)]); + const c = Validators.compose([validator('a', true), validator('b', true)]); expect(c(new FormControl(''))).toEqual({'a': true, 'b': true}); }); it('should run validators left to right', () => { - var c = Validators.compose([validator('a', 1), validator('a', 2)]); + const c = Validators.compose([validator('a', 1), validator('a', 2)]); expect(c(new FormControl(''))).toEqual({'a': 2}); }); it('should return null when no errors', () => { - var c = Validators.compose([Validators.nullValidator, Validators.nullValidator]); + const c = Validators.compose([Validators.nullValidator, Validators.nullValidator]); expect(c(new FormControl(''))).toBeNull(); }); it('should ignore nulls', () => { - var c = Validators.compose([null, Validators.required]); + const c = Validators.compose([null, Validators.required]); expect(c(new FormControl(''))).toEqual({'required': true}); }); }); diff --git a/tools/public_api_guard/forms/index.d.ts b/tools/public_api_guard/forms/index.d.ts index 95bd3d06a0..29174a048e 100644 --- a/tools/public_api_guard/forms/index.d.ts +++ b/tools/public_api_guard/forms/index.d.ts @@ -517,7 +517,7 @@ export declare class Validators { static nullValidator(c: AbstractControl): { [key: string]: boolean; }; - static pattern(pattern: string): ValidatorFn; + static pattern(pattern: string | RegExp): ValidatorFn; static required(control: AbstractControl): { [key: string]: boolean; };