feat(forms): introduce AsyncValidator interface (#13483)

Closes #13398
This commit is contained in:
Dzmitry Shylovich 2017-02-21 03:26:51 +03:00 committed by Victor Berchet
parent d6a58f9f70
commit 551fe50ebd
12 changed files with 43 additions and 25 deletions

View File

@ -10,7 +10,7 @@
import {AbstractControlDirective} from './abstract_control_directive';
import {ControlContainer} from './control_container';
import {ControlValueAccessor} from './control_value_accessor';
import {AsyncValidatorFn, Validator, ValidatorFn} from './validators';
import {AsyncValidator, AsyncValidatorFn, Validator, ValidatorFn} from './validators';
function unimplemented(): any {
throw new Error('unimplemented');
@ -32,7 +32,7 @@ export abstract class NgControl extends AbstractControlDirective {
/** @internal */
_rawValidators: Array<Validator|ValidatorFn> = [];
/** @internal */
_rawAsyncValidators: Array<Validator|ValidatorFn> = [];
_rawAsyncValidators: Array<AsyncValidator|AsyncValidatorFn> = [];
get validator(): ValidatorFn { return <ValidatorFn>unimplemented(); }
get asyncValidator(): AsyncValidatorFn { return <AsyncValidatorFn>unimplemented(); }

View File

@ -20,7 +20,7 @@ import {NgForm} from './ng_form';
import {NgModelGroup} from './ng_model_group';
import {composeAsyncValidators, composeValidators, controlPath, isPropertyUpdated, selectValueAccessor, setUpControl} from './shared';
import {TemplateDrivenErrors} from './template_driven_errors';
import {AsyncValidatorFn, Validator, ValidatorFn} from './validators';
import {AsyncValidator, AsyncValidatorFn, Validator, ValidatorFn} from './validators';
export const formControlBinding: any = {
provide: NgControl,
@ -136,7 +136,7 @@ export class NgModel extends NgControl implements OnChanges,
constructor(@Optional() @Host() parent: ControlContainer,
@Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>,
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: Array<Validator|AsyncValidatorFn>,
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: Array<AsyncValidator|AsyncValidatorFn>,
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR)
valueAccessors: ControlValueAccessor[]) {
super();

View File

@ -7,8 +7,7 @@
*/
import {AbstractControl} from '../model';
import {AsyncValidatorFn, Validator, ValidatorFn} from './validators';
import {AsyncValidator, AsyncValidatorFn, Validator, ValidatorFn} from './validators';
export function normalizeValidator(validator: ValidatorFn | Validator): ValidatorFn {
if ((<Validator>validator).validate) {
@ -18,9 +17,10 @@ export function normalizeValidator(validator: ValidatorFn | Validator): Validato
}
}
export function normalizeAsyncValidator(validator: AsyncValidatorFn | Validator): AsyncValidatorFn {
if ((<Validator>validator).validate) {
return (c: AbstractControl) => (<Validator>validator).validate(c);
export function normalizeAsyncValidator(validator: AsyncValidatorFn | AsyncValidator):
AsyncValidatorFn {
if ((<AsyncValidator>validator).validate) {
return (c: AbstractControl) => (<AsyncValidator>validator).validate(c);
} else {
return <AsyncValidatorFn>validator;
}

View File

@ -15,7 +15,7 @@ import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '../control_value_accessor
import {NgControl} from '../ng_control';
import {ReactiveErrors} from '../reactive_errors';
import {composeAsyncValidators, composeValidators, isPropertyUpdated, selectValueAccessor, setUpControl} from '../shared';
import {AsyncValidatorFn, Validator, ValidatorFn} from '../validators';
import {AsyncValidator, AsyncValidatorFn, Validator, ValidatorFn} from '../validators';
export const formControlBinding: any = {
provide: NgControl,
@ -77,7 +77,7 @@ export class FormControlDirective extends NgControl implements OnChanges {
set isDisabled(isDisabled: boolean) { ReactiveErrors.disabledAttrWarning(); }
constructor(@Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>,
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: Array<Validator|AsyncValidatorFn>,
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: Array<AsyncValidator|AsyncValidatorFn>,
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR)
valueAccessors: ControlValueAccessor[]) {
super();

View File

@ -17,7 +17,7 @@ import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '../control_value_accessor
import {NgControl} from '../ng_control';
import {ReactiveErrors} from '../reactive_errors';
import {composeAsyncValidators, composeValidators, controlPath, isPropertyUpdated, selectValueAccessor} from '../shared';
import {AsyncValidatorFn, Validator, ValidatorFn} from '../validators';
import {AsyncValidator, AsyncValidatorFn, Validator, ValidatorFn} from '../validators';
import {FormGroupDirective} from './form_group_directive';
import {FormArrayName, FormGroupName} from './form_group_name';
@ -98,7 +98,7 @@ export class FormControlName extends NgControl implements OnChanges, OnDestroy {
@Optional() @Host() @SkipSelf() parent: ControlContainer,
@Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>,
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators:
Array<Validator|AsyncValidatorFn>,
Array<AsyncValidator|AsyncValidatorFn>,
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[]) {
super();
this._parent = parent;

View File

@ -24,7 +24,7 @@ import {RangeValueAccessor} from './range_value_accessor';
import {FormArrayName} from './reactive_directives/form_group_name';
import {SelectControlValueAccessor} from './select_control_value_accessor';
import {SelectMultipleControlValueAccessor} from './select_multiple_control_value_accessor';
import {AsyncValidatorFn, Validator, ValidatorFn} from './validators';
import {AsyncValidator, AsyncValidatorFn, Validator, ValidatorFn} from './validators';
export function controlPath(name: string, parent: ControlContainer): string[] {
@ -68,7 +68,7 @@ export function setUpControl(control: FormControl, dir: NgControl): void {
(<Validator>validator).registerOnValidatorChange(() => control.updateValueAndValidity());
});
dir._rawAsyncValidators.forEach((validator: Validator | ValidatorFn) => {
dir._rawAsyncValidators.forEach((validator: AsyncValidator | AsyncValidatorFn) => {
if ((<Validator>validator).registerOnValidatorChange)
(<Validator>validator).registerOnValidatorChange(() => control.updateValueAndValidity());
});

View File

@ -5,7 +5,9 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, Input, OnChanges, Provider, SimpleChanges, forwardRef} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import {AbstractControl} from '../model';
import {NG_VALIDATORS, Validators} from '../validators';
@ -33,6 +35,11 @@ export interface Validator {
registerOnValidatorChange?(fn: () => void): void;
}
/** @experimental */
export interface AsyncValidator extends Validator {
validate(c: AbstractControl): Promise<{[key: string]: any}>|Observable<{[key: string]: any}>;
}
export const REQUIRED_VALIDATOR: Provider = {
provide: NG_VALIDATORS,
useExisting: forwardRef(() => RequiredValidator),

View File

@ -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, EmailValidator, MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator, Validator, ValidatorFn} from './directives/validators';
export {AsyncValidator, 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';

View File

@ -8,7 +8,7 @@
import {Component, Directive, EventEmitter, Input, Output, Type, forwardRef} from '@angular/core';
import {ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing';
import {AbstractControl, ControlValueAccessor, FormArray, FormControl, FormGroup, FormGroupDirective, FormsModule, NG_ASYNC_VALIDATORS, NG_VALIDATORS, NG_VALUE_ACCESSOR, NgControl, ReactiveFormsModule, Validator, Validators} from '@angular/forms';
import {AbstractControl, AsyncValidator, ControlValueAccessor, FormArray, FormControl, FormGroup, FormGroupDirective, FormsModule, NG_ASYNC_VALIDATORS, NG_VALIDATORS, NG_VALUE_ACCESSOR, NgControl, ReactiveFormsModule, Validators} from '@angular/forms';
import {By} from '@angular/platform-browser/src/dom/debug/by';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {dispatchEvent} from '@angular/platform-browser/testing/browser_util';
@ -1951,7 +1951,7 @@ class LoginIsEmptyValidator {
multi: true
}]
})
class UniqLoginValidator implements Validator {
class UniqLoginValidator implements AsyncValidator {
@Input('uniq-login-validator') expected: any;
validate(c: AbstractControl) { return uniqLoginAsyncValidator(this.expected)(c); }

View File

@ -8,7 +8,7 @@
import {Component, Directive, Input, Type, forwardRef} from '@angular/core';
import {ComponentFixture, TestBed, async, fakeAsync, tick} from '@angular/core/testing';
import {AbstractControl, ControlValueAccessor, FormsModule, NG_ASYNC_VALIDATORS, NG_VALUE_ACCESSOR, NgForm, Validator} from '@angular/forms';
import {AbstractControl, AsyncValidator, ControlValueAccessor, FormsModule, NG_ASYNC_VALIDATORS, NG_VALUE_ACCESSOR, NgForm} from '@angular/forms';
import {By} from '@angular/platform-browser/src/dom/debug/by';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {dispatchEvent} from '@angular/platform-browser/testing/browser_util';
@ -1493,7 +1493,7 @@ class NgModelEmailValidator {
{provide: NG_ASYNC_VALIDATORS, useExisting: forwardRef(() => NgAsyncValidator), multi: true}
]
})
class NgAsyncValidator implements Validator {
class NgAsyncValidator implements AsyncValidator {
validate(c: AbstractControl) { return Promise.resolve(null); }
}

View File

@ -12,6 +12,7 @@ import {AbstractControl, FormArray, FormControl, FormGroup, Validators} from '@a
import {Observable} from 'rxjs/Observable';
import {normalizeAsyncValidator} from '../src/directives/normalize_validator';
import {AsyncValidator} from '../src/directives/validators';
import {EventEmitter} from '../src/facade/async';
export function main() {
@ -23,10 +24,11 @@ export function main() {
};
}
class AsyncValidatorDirective {
class AsyncValidatorDirective implements AsyncValidator {
constructor(private expected: string, private error: any) {}
validate(c: any): {[key: string]: any;} {
validate(c: any): Observable < { [key: string]: any; }
> {
return Observable.create((obs: any) => {
const error = this.expected !== c.value ? this.error : null;
obs.next(error);

View File

@ -101,6 +101,15 @@ export declare class AbstractFormGroupDirective extends ControlContainer impleme
ngOnInit(): void;
}
/** @experimental */
export interface AsyncValidator extends Validator {
validate(c: AbstractControl): Promise<{
[key: string]: any;
}> | Observable<{
[key: string]: any;
}>;
}
/** @stable */
export interface AsyncValidatorFn {
(c: AbstractControl): any;
@ -253,7 +262,7 @@ export declare class FormControlDirective extends NgControl implements OnChanges
update: EventEmitter<{}>;
readonly validator: ValidatorFn;
viewModel: any;
constructor(validators: Array<Validator | ValidatorFn>, asyncValidators: Array<Validator | AsyncValidatorFn>, valueAccessors: ControlValueAccessor[]);
constructor(validators: Array<Validator | ValidatorFn>, asyncValidators: Array<AsyncValidator | AsyncValidatorFn>, valueAccessors: ControlValueAccessor[]);
ngOnChanges(changes: SimpleChanges): void;
viewToModelUpdate(newValue: any): void;
}
@ -269,7 +278,7 @@ export declare class FormControlName extends NgControl implements OnChanges, OnD
readonly path: string[];
update: EventEmitter<{}>;
readonly validator: ValidatorFn;
constructor(parent: ControlContainer, validators: Array<Validator | ValidatorFn>, asyncValidators: Array<Validator | AsyncValidatorFn>, valueAccessors: ControlValueAccessor[]);
constructor(parent: ControlContainer, validators: Array<Validator | ValidatorFn>, asyncValidators: Array<AsyncValidator | AsyncValidatorFn>, valueAccessors: ControlValueAccessor[]);
ngOnChanges(changes: SimpleChanges): void;
ngOnDestroy(): void;
viewToModelUpdate(newValue: any): void;
@ -434,7 +443,7 @@ export declare class NgModel extends NgControl implements OnChanges, OnDestroy {
update: EventEmitter<{}>;
readonly validator: ValidatorFn;
viewModel: any;
constructor(parent: ControlContainer, validators: Array<Validator | ValidatorFn>, asyncValidators: Array<Validator | AsyncValidatorFn>, valueAccessors: ControlValueAccessor[]);
constructor(parent: ControlContainer, validators: Array<Validator | ValidatorFn>, asyncValidators: Array<AsyncValidator | AsyncValidatorFn>, valueAccessors: ControlValueAccessor[]);
compositionEnd(): void;
compositionStart(): void;
ngOnChanges(changes: SimpleChanges): void;