feat(forms): add email validator (#13709)
Closes #13706 PR Close #13709
This commit is contained in:
parent
00979838ef
commit
d69717cf79
|
@ -24,7 +24,7 @@ import {FormGroupDirective} from './directives/reactive_directives/form_group_di
|
|||
import {FormArrayName, FormGroupName} from './directives/reactive_directives/form_group_name';
|
||||
import {NgSelectOption, SelectControlValueAccessor} from './directives/select_control_value_accessor';
|
||||
import {NgSelectMultipleOption, SelectMultipleControlValueAccessor} from './directives/select_multiple_control_value_accessor';
|
||||
import {CheckboxRequiredValidator, MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator} from './directives/validators';
|
||||
import {CheckboxRequiredValidator, EmailValidator, MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator} from './directives/validators';
|
||||
|
||||
export {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
|
||||
export {ControlValueAccessor} from './directives/control_value_accessor';
|
||||
|
@ -62,6 +62,7 @@ export const SHARED_FORM_DIRECTIVES: Type<any>[] = [
|
|||
MaxLengthValidator,
|
||||
PatternValidator,
|
||||
CheckboxRequiredValidator,
|
||||
EmailValidator,
|
||||
];
|
||||
|
||||
export const TEMPLATE_DRIVEN_DIRECTIVES: Type<any>[] = [NgModel, NgModelGroup, NgForm];
|
||||
|
|
|
@ -106,6 +106,50 @@ export class CheckboxRequiredValidator extends RequiredValidator {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider which adds {@link EmailValidator} to {@link NG_VALIDATORS}.
|
||||
*/
|
||||
export const EMAIL_VALIDATOR: any = {
|
||||
provide: NG_VALIDATORS,
|
||||
useExisting: forwardRef(() => EmailValidator),
|
||||
multi: true
|
||||
};
|
||||
|
||||
/**
|
||||
* A Directive that adds the `email` validator to controls marked with the
|
||||
* `email` attribute, via the {@link NG_VALIDATORS} binding.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* <input type="email" name="email" ngModel email>
|
||||
* <input type="email" name="email" ngModel email="true">
|
||||
* <input type="email" name="email" ngModel [email]="true">
|
||||
* ```
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[email][formControlName],[email][formControl],[email][ngModel]',
|
||||
providers: [EMAIL_VALIDATOR]
|
||||
})
|
||||
export class EmailValidator implements Validator {
|
||||
private _enabled: boolean;
|
||||
private _onChange: () => void;
|
||||
|
||||
@Input()
|
||||
set email(value: boolean|string) {
|
||||
this._enabled = value === '' || value === true || value === 'true';
|
||||
if (this._onChange) this._onChange();
|
||||
}
|
||||
|
||||
validate(c: AbstractControl): {[key: string]: any} {
|
||||
return this._enabled ? Validators.email(c) : null;
|
||||
}
|
||||
|
||||
registerOnValidatorChange(fn: () => void): void { this._onChange = fn; }
|
||||
}
|
||||
|
||||
/**
|
||||
* @stable
|
||||
*/
|
||||
|
|
|
@ -38,7 +38,7 @@ export {FormArrayName} from './directives/reactive_directives/form_group_name';
|
|||
export {FormGroupName} from './directives/reactive_directives/form_group_name';
|
||||
export {NgSelectOption, SelectControlValueAccessor} from './directives/select_control_value_accessor';
|
||||
export {SelectMultipleControlValueAccessor} from './directives/select_multiple_control_value_accessor';
|
||||
export {AsyncValidatorFn, CheckboxRequiredValidator, MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator, Validator, ValidatorFn} from './directives/validators';
|
||||
export {AsyncValidatorFn, CheckboxRequiredValidator, EmailValidator, MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator, Validator, ValidatorFn} from './directives/validators';
|
||||
export {FormBuilder} from './form_builder';
|
||||
export {AbstractControl, FormArray, FormControl, FormGroup} from './model';
|
||||
export {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from './validators';
|
||||
|
|
|
@ -45,6 +45,9 @@ export const NG_VALIDATORS = new InjectionToken<Array<Validator|Function>>('NgVa
|
|||
export const NG_ASYNC_VALIDATORS =
|
||||
new InjectionToken<Array<Validator|Function>>('NgAsyncValidators');
|
||||
|
||||
const EMAIL_REGEXP =
|
||||
/^(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+(\.[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$/;
|
||||
|
||||
/**
|
||||
* Provides a set of validators used by form controls.
|
||||
*
|
||||
|
@ -74,6 +77,13 @@ export class Validators {
|
|||
return control.value === true ? null : {'required': true};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validator that performs email validation.
|
||||
*/
|
||||
static email(control: AbstractControl): {[key: string]: boolean} {
|
||||
return EMAIL_REGEXP.test(control.value) ? null : {'email': true};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validator that requires controls to have a value of a minimum length.
|
||||
*/
|
||||
|
|
|
@ -859,6 +859,41 @@ export function main() {
|
|||
expect(control.hasError('required')).toBe(true);
|
||||
}));
|
||||
|
||||
it('should validate email', fakeAsync(() => {
|
||||
const fixture = initTest(NgModelEmailValidator);
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
const control =
|
||||
fixture.debugElement.children[0].injector.get(NgForm).control.get('email');
|
||||
|
||||
const input = fixture.debugElement.query(By.css('input'));
|
||||
expect(control.hasError('email')).toBe(false);
|
||||
|
||||
fixture.componentInstance.validatorEnabled = true;
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
expect(input.nativeElement.value).toEqual('');
|
||||
expect(control.hasError('email')).toBe(true);
|
||||
|
||||
input.nativeElement.value = 'test@gmail.com';
|
||||
dispatchEvent(input.nativeElement, 'input');
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
expect(input.nativeElement.value).toEqual('test@gmail.com');
|
||||
expect(control.hasError('email')).toBe(false);
|
||||
|
||||
input.nativeElement.value = 'text';
|
||||
dispatchEvent(input.nativeElement, 'input');
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
|
||||
expect(input.nativeElement.value).toEqual('text');
|
||||
expect(control.hasError('email')).toBe(true);
|
||||
}));
|
||||
|
||||
it('should support dir validators using bindings', fakeAsync(() => {
|
||||
const fixture = initTest(NgModelValidationBindings);
|
||||
fixture.componentInstance.required = true;
|
||||
|
@ -1335,6 +1370,14 @@ class NgModelCheckboxRequiredValidator {
|
|||
required: boolean = false;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'ng-model-email',
|
||||
template: `<form><input type="email" ngModel [email]="validatorEnabled" name="email"></form>`
|
||||
})
|
||||
class NgModelEmailValidator {
|
||||
validatorEnabled: boolean = false;
|
||||
}
|
||||
|
||||
@Directive({
|
||||
selector: '[ng-async-validator]',
|
||||
providers: [
|
||||
|
|
|
@ -64,6 +64,14 @@ export function main() {
|
|||
() => expect(Validators.requiredTrue(new FormControl(true))).toBeNull());
|
||||
});
|
||||
|
||||
describe('email', () => {
|
||||
it('should error on invalid email',
|
||||
() => expect(Validators.email(new FormControl('some text'))).toEqual({'email': true}));
|
||||
|
||||
it('should not error on valid email',
|
||||
() => expect(Validators.email(new FormControl('test@gmail.com'))).toBeNull());
|
||||
});
|
||||
|
||||
describe('minLength', () => {
|
||||
it('should not error on an empty string',
|
||||
() => { expect(Validators.minLength(2)(new FormControl(''))).toBeNull(); });
|
||||
|
|
|
@ -150,6 +150,15 @@ export declare class DefaultValueAccessor implements ControlValueAccessor {
|
|||
writeValue(value: any): void;
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
export declare class EmailValidator implements Validator {
|
||||
email: boolean | string;
|
||||
registerOnValidatorChange(fn: () => void): void;
|
||||
validate(c: AbstractControl): {
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
|
||||
/** @stable */
|
||||
export interface Form {
|
||||
addControl(dir: NgControl): void;
|
||||
|
@ -529,6 +538,9 @@ export interface ValidatorFn {
|
|||
export declare class Validators {
|
||||
static compose(validators: ValidatorFn[]): ValidatorFn;
|
||||
static composeAsync(validators: AsyncValidatorFn[]): AsyncValidatorFn;
|
||||
static email(control: AbstractControl): {
|
||||
[key: string]: boolean;
|
||||
};
|
||||
static maxLength(maxLength: number): ValidatorFn;
|
||||
static minLength(minLength: number): ValidatorFn;
|
||||
static nullValidator(c: AbstractControl): {
|
||||
|
|
Loading…
Reference in New Issue