gpt4 book ai didi

angular2 formBuilder 组异步验证

转载 作者:太空狗 更新时间:2023-10-29 18:06:59 27 4
gpt4 key购买 nike

我正在尝试实现一个异步验证器但没有成功......

我的组件创建了一个表单:

this.personForm = this._frmBldr.group({
lastname: [ '', Validators.compose([Validators.required, Validators.minLength(2) ]) ],
firstname: [ '', Validators.compose([Validators.required, Validators.minLength(2) ]) ],
birthdate: [ '', Validators.compose([ Validators.required, DateValidators.checkIsNotInTheFuture ]) ],
driverLicenceDate: [ '', Validators.compose([ Validators.required, DateValidators.checkIsNotInTheFuture ]), this.asyncValidationLicenceDate.bind(this) ],
}, {
asyncValidator: this.validateBusiness.bind(this),
validator: this.validateDriverLicenseOlderThanBirthdate,
});

我的验证方法
validateBusiness(group: FormGroup) {
console.log('validateBusiness')
return this._frmService
.validateForm(group.value)
.map((validationResponse: IValidationResponse) => {
if (validationResponse) {
validationResponse.validations.forEach( (validationError: IValidationErrorDescription) => {
let errorMsg = validationError.display;
let errorCode = validationError.code;
validationError.fields.forEach( (fieldName: string) => {
console.log(fieldName);
let control = this.personForm.controls[fieldName];
let existingErrors = control.errors || {};
existingErrors[errorCode] = errorMsg;
control.setErrors(existingErrors);
});
});
}
});
}

所有验证都被称为成功,除了 validateBusiness 方法(在 extra.asyncValidatorformbuilder.group 参数中)从未被调用过......有人能告诉我我做错了什么吗?

发送

最佳答案

TL;博士:通过分析您的用例,您可能需要 解决方案 2

问题

问题在于如何定义和使用异步验证器。

异步验证器定义为:

export interface AsyncValidatorFn {
(c: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null>;
}

这是因为 FormBuilder.group()实际上是在调用 FormGroup构造函数:
constructor(controls: {
[key: string]: AbstractControl;
}, validator?: ValidatorFn | null, asyncValidator?: AsyncValidatorFn | null);

因此异步验证器函数将收到 AbstractControl实例,在这种情况下,是 FormGroup例如,因为验证器位于 FormGroup等级。验证器需要返回 PromiseObservableValidationErrors ,如果不存在验证错误,则为 null。
ValidationErrors被定义为字符串键和值的映射(任何你喜欢的)。键实际上是定义验证错误类型的字符串(例如:“required”)。
export declare type ValidationErrors = {
[key: string]: any;
};

AbstractControl.setErrors()? - 在您的示例中,您正在定义一个不返回任何内容但实际上直接更改控制错误的函数。调用 setErrors仅适用于手动调用验证并因此只能手动设置错误的情况。相反,在您的示例中,这些方法是混合的, FormControl s 附加了将自动运行的验证功能,以及 FormGroup异步验证功能也自动运行,尝试手动设置错误并因此设置有效性。这是行不通的。

您需要采用以下两种方法之一:
  • 附加将自动运行的验证功能,从而设置错误和有效性。不要尝试在附加了验证功能的控件上手动设置任何内容。
  • 手动设置错误并因此设置有效性,而不将任何验证功能附加到受影响的 AbstractControl实例。

  • 如果你想保持一切干净,那么你可以实现单独的验证功能。 FormControl验证将只处理一个控件。 FormGroup验证会将表单组的多个方面视为一个整体。

    如果您想使用验证服务,它实际上验证整个表单,就像您所做的那样,然后将每个错误委托(delegate)给每个适当的控件验证器,那么您可以使用 解决方案 2 .这有点困难。

    但是,如果您同意在 FormGroup 上有一个验证器使用您的验证服务的级别,那么这可以使用 来实现解决方案 1 .

    解决方案 1 - 在 FormGroup 级别创建错误

    假设我们要输入名字和姓氏,但名字需要与姓氏不同。并假设此计算需要 1 秒。

    模板
    <form [formGroup]="personForm">
    <div>
    <input type="text" name="firstName" formControlName="firstName" placeholder="First Name" />
    </div>
    <div>
    <input type="text" name="lastName" formControlName="lastName" placeholder="Last Name" />
    </div>

    <p style="color: red" *ngIf="personForm.errors?.sameValue">First name and last name should not be the same.</p>

    <button type="submit">Submit</button>
    </form>

    组件

    以下 validateBusiness验证函数将返回 Promise :
    import { Component, OnInit } from '@angular/core';
    import {AbstractControl, FormBuilder, FormGroup, ValidationErrors, Validators} from "@angular/forms";
    import {Observable} from "rxjs/Observable";
    import "rxjs/add/operator/delay";
    import "rxjs/add/operator/map";
    import "rxjs/add/observable/from";

    @Component({
    selector: 'app-async-validation',
    templateUrl: './async-validation.component.html',
    styleUrls: ['./async-validation.component.css']
    })
    export class AsyncValidationComponent implements OnInit {

    personForm: FormGroup;

    constructor(private _formBuilder: FormBuilder) { }

    ngOnInit() {

    this.personForm = this._formBuilder.group({
    firstName: [ '', Validators.required ],
    lastName: [ '', Validators.required ],
    }, {
    asyncValidator: this.validateBusiness.bind(this)
    });
    }

    validateBusiness(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {

    return new Promise((resolve, reject) => {
    setTimeout(() => {
    if (control.value.firstName !== control.value.lastName) {
    resolve(null);
    }
    else {
    resolve({sameValue: 'ERROR...'});
    }
    },
    1000);
    });
    }
    }

    或者,验证函数可以返回 Observable :
      validateBusiness(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {

    return Observable
    .from([control.value.firstName !== control.value.lastName])
    .map(valid => valid ? null : {sameValue: 'ERROR...'})
    .delay(1000);
    }

    解决方案 2 - 协调多个控件的验证错误

    另一种选择是在表单更改时手动验证,然后将结果传递给 observable,稍后可以由 FormGroup 使用。和 FormControl异步验证器。

    我创建了一个 POC here .

    IValidationResponse

    来自用于验证表单数据的验证服务的响应。
    import {IValidationErrorDescription} from "./IValidationErrorDescription";

    export interface IValidationResponse {
    validations: IValidationErrorDescription[];
    }

    IValidationErrorDescription

    验证响应错误描述。
    export interface IValidationErrorDescription {
    display: string;
    code: string;
    fields: string[];
    }

    业务验证服务

    验证服务,实现表单数据验证业务。
    import { Injectable } from '@angular/core';
    import {Observable} from 'rxjs/Observable';
    import 'rxjs/add/observable/from';
    import 'rxjs/add/operator/map';
    import {IValidationResponse} from "../model/IValidationResponse";

    @Injectable()
    export class BusinessValidationService {

    public validateForm(value: any): Observable<IValidationResponse> {
    return Observable
    .from([value.firstName !== value.lastName])
    .map(valid => valid ?
    {validations: []}
    :
    {
    validations: [
    {
    code: 'sameValue',
    display: 'First name and last name are the same',
    fields: ['firstName', 'lastName']
    }
    ]
    }
    )
    .delay(500);
    }
    }

    表单验证服务

    用于为 FormGroup 构建异步验证器的验证服务和 FormControl并订阅表单数据的更改,以便将验证委托(delegate)给验证回调(例如: BusinessValidationService )。

    它提供以下内容:
  • validateFormOnChange() - 当表单更改时,它会调用验证回调 validateFormCallback以及何时触发 FormGroup 的验证和 FormControl s 使用 control.validateFormGroup() .
  • createGroupAsyncValidator() - 为 FormGroup 创建一个异步验证器
  • createControlAsyncValidator() - 为 FormControl 创建一个异步验证器

  • 编码:
    import { Injectable } from '@angular/core';
    import {Observable} from 'rxjs/Observable';
    import 'rxjs/add/observable/from';
    import 'rxjs/add/operator/switchMap';
    import 'rxjs/add/operator/first';
    import 'rxjs/add/operator/share';
    import 'rxjs/add/operator/debounceTime';
    import {AbstractControl, AsyncValidatorFn, FormGroup} from '@angular/forms';
    import {ReplaySubject} from 'rxjs/ReplaySubject';
    import {IValidationResponse} from "../model/IValidationResponse";

    @Injectable()
    export class FormValidationService {

    private _subject$ = new ReplaySubject<IValidationResponse>(1);
    private _validationResponse$ = this._subject$.debounceTime(100).share();
    private _oldValue = null;

    constructor() {
    this._subject$.subscribe();
    }

    public get onValidate(): Observable<IValidationResponse> {
    return this._subject$.map(response => response);
    }

    public validateFormOnChange(group: FormGroup, validateFormCallback: (value: any) => Observable<IValidationResponse>) {
    group.valueChanges.subscribe(value => {
    const isChanged = this.isChanged(value, this._oldValue);
    this._oldValue = value;

    if (!isChanged) {
    return;
    }

    this._subject$.next({validations: []});
    this.validateFormGroup(group);

    validateFormCallback(value).subscribe(validationRes => {
    this._subject$.next(validationRes);
    this.validateFormGroup(group);
    });
    });
    }

    private isChanged(newValue, oldValue): boolean {
    if (!newValue) {
    return true;
    }

    return !!Object.keys(newValue).find(key => !oldValue || newValue[key] !== oldValue[key]);
    }

    private validateFormGroup(group: FormGroup) {
    group.updateValueAndValidity({ emitEvent: true, onlySelf: false });

    Object.keys(group.controls).forEach(controlName => {
    group.controls[controlName].updateValueAndValidity({ emitEvent: true, onlySelf: false });
    });
    }

    public createControlAsyncValidator(fieldName: string): AsyncValidatorFn {
    return (control: AbstractControl) => {
    return this._validationResponse$
    .switchMap(validationRes => {
    const errors = validationRes.validations
    .filter(validation => validation.fields.indexOf(fieldName) >= 0)
    .reduce((errorMap, validation) => {
    errorMap[validation.code] = validation.display;
    return errorMap;
    }, {});

    return Observable.from([errors]);
    })
    .first();
    };
    }

    public createGroupAsyncValidator(): AsyncValidatorFn {
    return (control: AbstractControl) => {

    return this._validationResponse$
    .switchMap(validationRes => {
    const errors = validationRes.validations
    .reduce((errorMap, validation) => {
    errorMap[validation.code] = validation.display;
    return errorMap;
    }, {});

    return Observable.from([errors]);
    })
    .first();
    };
    }
    }

    AsyncFormValidateComponent 模板

    定义 firstNamelastName FormControl s 位于 personFormFormGroup .对于此示例,条件是 firstNamelastName应该不同。
    <form [formGroup]="personForm">
    <div>
    <label for="firstName">First name:</label>

    <input type="text"
    id="firstName"
    name="firstName"
    formControlName="firstName"
    placeholder="First Name" />

    <span *ngIf="personForm.controls['firstName'].errors?.sameValue">Same as last name</span>
    </div>
    <div>
    <label for="lastName">Last name:</label>

    <input type="text"
    id="lastName"
    name="lastName"
    formControlName="lastName"
    placeholder="Last Name" />

    <span *ngIf="personForm.controls['lastName'].errors?.sameValue">Same as first name</span>
    </div>

    <p style="color: red" *ngIf="personForm.errors?.sameValue">First name and last name should not be the same.</p>

    <button type="submit">Submit</button>
    </form>

    AsyncValidateFormComponent

    用作示例的组件使用 FrmValidationService 实现验证.由于 providers: [FormValidationService],此组件有自己的此服务实例.由于 Angular 分层注入(inject)器功能,一个注入(inject)器将与此组件相关联,并且将为 AsyncValidateFormComponent 的每个实例创建此服务的一个实例。 .因此能够在每个组件实例的基础上跟踪此服务内部的验证状态。
    import { Component, OnInit } from '@angular/core';
    import {FormBuilder, FormGroup, Validators} from '@angular/forms';
    import 'rxjs/add/operator/delay';
    import 'rxjs/add/operator/map';
    import 'rxjs/add/observable/from';
    import {FormValidationService} from "../services/form-validation.service";
    import {BusinessValidationService} from "../services/business-validation.service";

    @Component({
    selector: 'app-async-validate-form',
    templateUrl: './async-validate-form.component.html',
    styleUrls: ['./async-validate-form.component.css'],
    providers: [FormValidationService]
    })
    export class AsyncValidateFormComponent implements OnInit {

    personForm: FormGroup;

    constructor(private _formBuilder: FormBuilder,
    private _formValidationService: FormValidationService,
    private _businessValidationService: BusinessValidationService) {
    }

    ngOnInit() {
    this.personForm = this._formBuilder.group({
    firstName: ['', Validators.required, this._formValidationService.createControlAsyncValidator('firstName')],
    lastName: ['', Validators.required, this._formValidationService.createControlAsyncValidator('lastName')],
    }, {
    asyncValidator: this._formValidationService.createGroupAsyncValidator()
    });

    this._formValidationService.validateFormOnChange(this.personForm, value => this._businessValidationService.validateForm(value));
    }
    }

    应用模块

    它使用 ReactiveFormsModule为了与 FormBuilder 一起工作, FormGroupFormControl .还提供了 BusinessValidationService .
    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import {FormsModule, ReactiveFormsModule} from '@angular/forms';
    import { HttpModule } from '@angular/http';

    import { AppComponent } from './app.component';
    import { AsyncValidateFormComponent } from './async-validate-form/async-validate-form.component';
    import {BusinessValidationService} from "./services/business-validation.service";

    @NgModule({
    declarations: [
    AppComponent,
    AsyncValidateFormComponent
    ],
    imports: [
    BrowserModule,
    FormsModule,
    ReactiveFormsModule,
    HttpModule
    ],
    providers: [
    BusinessValidationService
    ],
    bootstrap: [AppComponent]
    })
    export class AppModule { }

    关于angular2 formBuilder 组异步验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44134892/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com