From 624a26f8f9dd5fca6bc1dca2d7b0f25e2513a65f Mon Sep 17 00:00:00 2001 From: Chris Griffith Date: Wed, 25 Jan 2017 11:01:51 -0800 Subject: [PATCH 1/3] Improved US Phone Number formats --- src/custom-validators.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/custom-validators.ts b/src/custom-validators.ts index a9328a0..d7f4220 100644 --- a/src/custom-validators.ts +++ b/src/custom-validators.ts @@ -231,7 +231,7 @@ export class CustomValidators { 'pt-PT': /^(\+351)?9[1236]\d{7}$/, 'el-GR': /^(\+?30)?(69\d{8})$/, 'en-GB': /^(\+?44|0)7\d{9}$/, - 'en-US': /^(\+?1)?[2-9]\d{2}[2-9](?!11)\d{6}$/, + 'en-US': /^(\(?[0-9]{3}\)?)((\s|\-){1})?[0-9]{3}((\s|\-){1})?[0-9]{4}$/, 'en-ZM': /^(\+26)?09[567]\d{7}$/, 'ru-RU': /^(\+?7|8)?9\d{9}$/, 'nb-NO': /^(\+?47)?[49]\d{7}$/, From 99b5fd41ea44bbfbedb006e67bc4f82336b422be Mon Sep 17 00:00:00 2001 From: Chris Griffith Date: Thu, 9 Feb 2017 09:55:05 -0800 Subject: [PATCH 2/3] Adding tests --- src/specs/custom-validators.spec.ts | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/specs/custom-validators.spec.ts b/src/specs/custom-validators.spec.ts index e8be055..acd05ae 100644 --- a/src/specs/custom-validators.spec.ts +++ b/src/specs/custom-validators.spec.ts @@ -441,4 +441,37 @@ describe('Custom Validators phone,', () => { }); }); + + describe('locale: en-US,', () => { + + beforeEach(() => { + validator = CustomValidators.phone("en-US"); + }); + + function phoneDataProvider() { + return [ + {phone: '888 555 2112', valid: true}, + {phone: '888 555 2112', valid: true}, + {phone: '(888) 555-2112', valid: true}, + {phone: '(888) 555 2112', valid: true}, + {phone: '888-555-2112', valid: true}, + {phone: '888-555 2112', valid: true}, + {phone: '8885552112', valid: true} + ] + } + + using(phoneDataProvider, (data) => { + let testCase = data.phone + ' should be ' + (data.valid ? 'valid' : 'invalid'); + + it(testCase, () => { + control = new FormControl(data.phone); + + if (data.valid) + return expect(validator(control)).toBeNull(); + + expect(validator(control)).toEqual(error); + }) + }); + + }); }); From 60f3d84580fbdaa882744ccebef47895355c6a4b Mon Sep 17 00:00:00 2001 From: Chris Griffith Date: Thu, 9 Feb 2017 10:19:32 -0800 Subject: [PATCH 3/3] Merges and tests --- README.md | 32 +++++++ example/src/app.component.ts | 2 +- example/src/app.html | 9 +- package.json | 2 +- src/custom-validators.ts | 33 ++++++- src/directives.ts | 4 +- src/directives/not-equal-to.ts | 28 ++++++ src/specs/custom-validators.spec.ts | 142 ++++++++++++++++++++++++++-- 8 files changed, 236 insertions(+), 16 deletions(-) create mode 100644 src/directives/not-equal-to.ts diff --git a/README.md b/README.md index 684dd94..5e138a3 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ npm install ng2-validation --save - uuid - equal - equalTo +- notEqualTo # Usage @@ -193,6 +194,7 @@ export class AppModule { - nb-NO - nl-NL - nn-NO +- pt-BR - pt-PT - ru-RU - vi-VN @@ -231,6 +233,15 @@ export class AppModule {

equalTo error

``` +### notEqualTo + +```html + +

required error

+ +

equalTo error

+``` + ## model driven import `ReactiveFormsModule` in *app.module.ts* @@ -407,6 +418,27 @@ this.form = new FormGroup({ ``` +### notEqualTo + +```javascript +let password = new FormControl('', Validators.required); +let certainPassword = new FormControl('', CustomValidators.notEqualTo(password)); + +this.form = new FormGroup({ + password: password, + certainPassword: certainPassword +}); +``` + +```html +
+ +

required error

+ +

notEqualTo error

+
+``` + # License MIT diff --git a/example/src/app.component.ts b/example/src/app.component.ts index 4973eb8..9f4cbfb 100644 --- a/example/src/app.component.ts +++ b/example/src/app.component.ts @@ -13,7 +13,7 @@ export class AppComponent implements OnInit { ngOnInit() { let password = new FormControl('', Validators.required); - let certainPassword = new FormControl('', CustomValidators.equalTo(password)); + let certainPassword = new FormControl('', CustomValidators.notEqualTo(password)); this.form = new FormGroup({ password: password, diff --git a/example/src/app.html b/example/src/app.html index 828365a..2573b33 100644 --- a/example/src/app.html +++ b/example/src/app.html @@ -62,4 +62,11 @@

minDate

- \ No newline at end of file + + +
+ +

required error

+ +

notEqualTo error

+
\ No newline at end of file diff --git a/package.json b/package.json index 81db6c2..8cd78dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ng2-validation", - "version": "3.1.3", + "version": "3.3.0", "description": "angular2 validation", "main": "dist/index.js", "typings": "dist/index.d.ts", diff --git a/src/custom-validators.ts b/src/custom-validators.ts index d7f4220..5834897 100644 --- a/src/custom-validators.ts +++ b/src/custom-validators.ts @@ -105,7 +105,10 @@ export class CustomValidators { * Validator that requires controls to have a value of minDate. */ static minDate(minDate: any): ValidatorFn { - if (!isDate(minDate)) throw Error('minDate value must be a formatted date'); + + if (!isDate(minDate) && !(minDate instanceof Function)) { + throw Error('minDate value must be or return a formatted date'); + } return (control: AbstractControl): {[key: string]: any} => { if (isPresent(Validators.required(control))) return null; @@ -113,6 +116,7 @@ export class CustomValidators { let d: Date = new Date(control.value); if (!isDate(d)) return {minDate: true}; + if (minDate instanceof Function) minDate = minDate(); return d >= new Date(minDate) ? null : {minDate: true}; }; @@ -122,7 +126,9 @@ export class CustomValidators { * Validator that requires controls to have a value of maxDate. */ static maxDate(maxDate: any): ValidatorFn { - if (!isDate(maxDate)) throw Error('maxDate value must be a formatted date'); + if (!isDate(maxDate) && !(maxDate instanceof Function)) { + throw Error('maxDate value must be or return a formatted date'); + } return (control: AbstractControl): {[key: string]: any} => { if (isPresent(Validators.required(control))) return null; @@ -130,6 +136,7 @@ export class CustomValidators { let d: Date = new Date(control.value); if (!isDate(d)) return {maxDate: true}; + if (maxDate instanceof Function) maxDate = maxDate(); return d <= new Date(maxDate) ? null : {maxDate: true}; }; @@ -240,7 +247,8 @@ export class CustomValidators { 'en-NZ': /^(\+?64|0)2\d{7,9}$/, 'hu-HU': /^(?:\+?(?:36|\(36\)))[ -\/]?(?:(?:(?:(?!1|20|21|30|31|70|90)[2-9][0-9])[ -\/]?\d{3}[ -\/]?\d{3})|(?:(?:1|20|21|30|31|70|90)[ -\/]?\d{3}[ -\/]?\d{2}[ -\/]?\d{2}))$/, 'nl-NL': /^(^\+[0-9]{2}|^\+[0-9]{2}\(0\)|^\(\+[0-9]{2}\)\(0\)|^00[0-9]{2}|^0)([0-9]{9}$|[0-9\-\s]{10}$)$/, - 'de-CH': /^(((\+|00)?41)?([ ])?(\(?0?\)?))([1-9]{2})(([ ])?[0-9]{3})(([ ])?[0-9]{2})(([ ])?[0-9]{2})$/ + 'de-CH': /^(((\+|00)?41)?([ ])?(\(?0?\)?))([1-9]{2})(([ ])?[0-9]{3})(([ ])?[0-9]{2})(([ ])?[0-9]{2})$/, + 'pt-BR': /^(\+?55[-\s]?)?(\([1-9][1-9]\)|[1-9][1-9])[-\s]?(9[1-9]\d{3}[-\s]?\d{4})$/ }; return (control: AbstractControl): {[key: string]: any} => { @@ -305,4 +313,23 @@ export class CustomValidators { return equalControl.value === v ? null : {equalTo: true}; }; } + + /** + * Validator that requires controls to have a value to not equal another control. + */ + static notEqualTo(notEqualControl: AbstractControl): ValidatorFn { + let subscribe: boolean = false; + return (control: AbstractControl): {[key: string]: any} => { + if (!subscribe) { + subscribe = true; + notEqualControl.valueChanges.subscribe(() => { + control.updateValueAndValidity(); + }); + } + + let v: string = control.value; + + return notEqualControl.value !== v ? null : {notEqualTo: true}; + }; + } } diff --git a/src/directives.ts b/src/directives.ts index 130c3c8..b79368f 100644 --- a/src/directives.ts +++ b/src/directives.ts @@ -19,6 +19,7 @@ import { PhoneValidator } from './directives/phone'; import { UUIDValidator } from './directives/uuid'; import { EqualValidator } from './directives/equal'; import { EqualToValidator } from './directives/equal-to'; +import { NotEqualToValidator } from './directives/not-equal-to'; export const CUSTOM_FORM_DIRECTIVES: Directive[] = [ RangeLengthValidator, @@ -39,7 +40,8 @@ export const CUSTOM_FORM_DIRECTIVES: Directive[] = [ PhoneValidator, UUIDValidator, EqualValidator, - EqualToValidator + EqualToValidator, + NotEqualToValidator, ]; @NgModule({ diff --git a/src/directives/not-equal-to.ts b/src/directives/not-equal-to.ts new file mode 100644 index 0000000..2a5001c --- /dev/null +++ b/src/directives/not-equal-to.ts @@ -0,0 +1,28 @@ +import { Directive, Input, forwardRef, OnInit } from '@angular/core'; +import { NG_VALIDATORS, Validator, FormControl, ValidatorFn, AbstractControl } from '@angular/forms'; + +import { CustomValidators } from '../index'; + +const NOT_EQUAL_TO_VALIDATOR: any = { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => NotEqualToValidator), + multi: true +}; + +@Directive({ + selector: '[notEqualTo][formControlName],[notEqualTo][formControl],[notEqualTo][ngModel]', + providers: [NOT_EQUAL_TO_VALIDATOR] +}) +export class NotEqualToValidator implements Validator, OnInit { + @Input() notEqualTo: FormControl; + + private validator: ValidatorFn; + + ngOnInit() { + this.validator = CustomValidators.notEqualTo(this.notEqualTo); + } + + validate(c: AbstractControl): {[key: string]: any} { + return this.validator(c); + } +} diff --git a/src/specs/custom-validators.spec.ts b/src/specs/custom-validators.spec.ts index acd05ae..6381382 100644 --- a/src/specs/custom-validators.spec.ts +++ b/src/specs/custom-validators.spec.ts @@ -136,22 +136,45 @@ describe('Custom Validators minDate,', () => { let control: FormControl; let validator: ValidatorFn; - beforeEach(() => { - validator = CustomValidators.minDate('2016-09-09'); - }); - it('"" should equal to "null"', () => { control = new FormControl(''); + validator = CustomValidators.minDate('2016-09-09'); expect(validator(control)).toBeNull(); }); it('"2016-09-08" should equal to "{minDate: true}"', () => { control = new FormControl('2016-09-08'); + validator = CustomValidators.minDate('2016-09-09'); expect(validator(control)).toEqual({minDate: true}); }); it('"2016-09-10" should equal to "null"', () => { control = new FormControl('2016-09-10'); + validator = CustomValidators.minDate('2016-09-09'); + expect(validator(control)).toBeNull(); + }); + + it('Date("2016-09-08)" should equal to "{minDate: true}"', () => { + control = new FormControl('2016-09-08'); + validator = CustomValidators.minDate(new Date('2016-09-09')); + expect(validator(control)).toEqual({minDate: true}); + }); + + it('"Date(2016-09-10)" should equal to "null"', () => { + control = new FormControl('2016-09-10'); + validator = CustomValidators.minDate(new Date('2016-09-09')); + expect(validator(control)).toBeNull(); + }); + + it('() => Date("2016-09-08)" should equal to "{minDate: true}"', () => { + control = new FormControl('2016-09-08'); + validator = CustomValidators.minDate(() => new Date('2016-09-09')); + expect(validator(control)).toEqual({minDate: true}); + }); + + it('"() => Date(2016-09-10)" should equal to "null"', () => { + control = new FormControl('2016-09-10'); + validator = CustomValidators.minDate(() => new Date('2016-09-09')); expect(validator(control)).toBeNull(); }); }); @@ -160,22 +183,45 @@ describe('Custom Validators maxDate,', () => { let control: FormControl; let validator: ValidatorFn; - beforeEach(() => { - validator = CustomValidators.maxDate('2016-09-09'); - }); - it('"" should equal to "null"', () => { control = new FormControl(''); + validator = CustomValidators.maxDate('2016-09-09'); expect(validator(control)).toBeNull(); }); it('"2016-09-10" should equal to "{maxDate: true}"', () => { control = new FormControl('2016-09-10'); + validator = CustomValidators.maxDate('2016-09-09'); expect(validator(control)).toEqual({maxDate: true}); }); it('"2016-09-08" should equal to "null"', () => { control = new FormControl('2016-09-08'); + validator = CustomValidators.maxDate('2016-09-09'); + expect(validator(control)).toBeNull(); + }); + + it('"Date(2016-09-10)" should equal to "{maxDate: true}"', () => { + control = new FormControl('2016-09-10'); + validator = CustomValidators.maxDate(new Date('2016-09-09')); + expect(validator(control)).toEqual({maxDate: true}); + }); + + it('"Date(2016-09-08)" should equal to "null"', () => { + control = new FormControl('2016-09-08'); + validator = CustomValidators.maxDate(new Date('2016-09-09')); + expect(validator(control)).toBeNull(); + }); + + it('"Date(2016-09-10)" should equal to "{maxDate: true}"', () => { + control = new FormControl('2016-09-10'); + validator = CustomValidators.maxDate(() => new Date('2016-09-09')); + expect(validator(control)).toEqual({maxDate: true}); + }); + + it('"Date(2016-09-08)" should equal to "null"', () => { + control = new FormControl('2016-09-08'); + validator = CustomValidators.maxDate(() => new Date('2016-09-09')); expect(validator(control)).toBeNull(); }); }); @@ -348,6 +394,45 @@ describe('Custom Validators EqualTo,', () => { }); +describe('Custom Validators NotEqualTo,', () => { + let notEqualControl: FormControl; + let control: FormControl; + const error = {notEqualTo: true}; + + beforeEach(() => { + notEqualControl = new FormControl(); + control = new FormControl(); + }); + + it('all control is empty should valid', () => { + expect(CustomValidators.notEqualTo(notEqualControl)(control)).toEqual(error); + }); + + it('control.value = "xxx" and notEqualControl.value is empty should valid', () => { + control.setValue('xxx'); + expect(CustomValidators.notEqualTo(notEqualControl)(control)).toBeNull(); + }); + + it('control.value = "xxx" and notEqualControl.value = "yyy" should valid', () => { + control.setValue('xxx'); + notEqualControl.setValue('yyy'); + expect(CustomValidators.notEqualTo(notEqualControl)(control)).toBeNull(); + }); + + it('control.value = "xxx" and notEqualControl.value = "xxx" should equal to "{notEqualTo: true}"', () => { + control.setValue('xxx'); + notEqualControl.setValue('xxx'); + expect(CustomValidators.notEqualTo(notEqualControl)(control)).toEqual(error); + }); + + it('control.value is empty and notEqualControl.value = "yyy" should valid', () => { + control.setValue(''); + notEqualControl.setValue('yyy'); + expect(CustomValidators.notEqualTo(notEqualControl)(control)).toBeNull(); + }); + +}); + describe('Custom Validators phone,', () => { let control: FormControl; let validator: ValidatorFn; @@ -442,6 +527,45 @@ describe('Custom Validators phone,', () => { }); + describe('locale: pt-BR,', () => { + + beforeEach(() => { + validator = CustomValidators.phone("pt-BR"); + }); + + function phoneDataProvider() { + return [ + {phone: '+55 11 98765-4321', valid: true}, + {phone: '+55 (11) 98765-4321', valid: true}, + {phone: '+55-11-98765-4321', valid: true}, + {phone: '+55 11 98765 4321', valid: true}, + {phone: '+5511987654321', valid: true}, + {phone: '5511987654321', valid: true}, + {phone: '+55 11 88765-4321', valid: false}, + {phone: '+55 (11) 8765-4321', valid: false}, + {phone: '+55-11-98765-432', valid: false}, + {phone: '+55 1 98765 4321', valid: false}, + {phone: '+511987654321', valid: false}, + {phone: '55119876543212', valid: false}, + {phone: '+54 11 98765-4321', valid: false} + ] + } + + using(phoneDataProvider, (data) => { + let testCase = data.phone + ' should be ' + (data.valid ? 'valid' : 'invalid'); + + it(testCase, () => { + control = new FormControl(data.phone); + + if (data.valid) + return expect(validator(control)).toBeNull(); + + expect(validator(control)).toEqual(error); + }) + }); + + }); + describe('locale: en-US,', () => { beforeEach(() => { @@ -474,4 +598,4 @@ describe('Custom Validators phone,', () => { }); }); -}); +}); \ No newline at end of file