单击按钮时使用 Reactive 表单进行异步验证

Async validation with Reactive forms on button click

我正在尝试验证数据库中是否存在名称。如果是这样,我想将其标记为无效并通知用户这种情况。

除此之外,我正在寻找执行此操作的最佳方法:模板驱动表单或反应式表单。

请注意,我使用了多种形式,如 https://material.angular.io 中的示例。

这是我当前的代码:

HTML:

<mat-horizontal-stepper [@.disabled]="true" #stepper>
  <mat-step label="Example" [stepControl]="nameForm">
    <form [formGroup]="nameForm">
      <mat-form-field>
        <mat-label>NAME</mat-label>
        <input matInput placeholder="Input name" formControlName="name">
      </mat-form-field>
      <button
        matTooltip="Verify name in data base"
        (click)="validateName()" <<<< LOOK, HERE IS THE POINT
        [disabled]="!loading">Validate Name
        <i class="ml-2 fa fa-refresh fa-spin" *ngIf="!loading"></i>
      </button>

      <div>
        <button mat-button matStepperNext>Next</button>
      </div>
    </form>
  </mat-step>

  <mat-step label="Endereço" [stepControl]="enderecoForm">
    <form [formGroup]="nameForm2">
      <mat-form-field>
        ...
      </mat-form-field>
    </form>
  </mat-step>

... more steps with one form inside of each one

TS:

export class SomeComponent {
  constructor(private readonly _formBuilder: FormBuilder) {}

  ngOnInit(): void {
    this.nameForm = this._formBuilder.group(
      {
        name: ['', Validators.required],
      },
      { validator: this.validateName },
    ); // AND HERE LOOK
    this.nameForm2 = this._formBuilder.group({
      name2: ['', Validators.required],
      // ... more forms and controls validators with reactive forms
    });
  }

  validateName(fb: FormGroup) {
    const nameCtrl = fb.get('name');
    // method verifyName return the name from database if exists
    const name = this.nameService.verifyName(nameCtrl);

    if (nameCtrl.errors == null || 'exists' in nameCtrl.errors) {
      if (name == null) {
        nameCtrl.setErrors(null);
      } else {
        nameCtrl.setErrors({ exists: true });
      }
    }
  }
}

我不太明白你是想使用 AsyncValidator 还是在点击按钮后进行验证,但我们开始吧:

1。通过 asyncValidators:

验证
import { Component } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';

import { Observable, of as just } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: 'app-some',
  templateUrl: './some-component.html'
})
export class SomeComponent {
  readonly formGroup = this.formBuilder.group(
    {
      name: ['', Validators.required, this.validateName]
    },
    { updateOn: 'blur' }
  );

  constructor(private readonly formBuilder: FormBuilder) {}

  // For this case, I'd suggest you to name it something like `nameAsyncValidator`.
  // In any case it's up to you :)
  private validateName(): Observable<{ exists: boolean } | null> {
    const { errors, value } = this.formGroup.get('name')!;

    if (errors || !value) return just(null);

    return this.nameService.verifyName(value).pipe(
      catchError(() => just(null))
    );
  }
}

2。要通过单击按钮进行验证:

import { ChangeDetectionStrategy, Component } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';

import { catchError, take, tap } from 'rxjs/operators';

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: 'app-some',
  templateUrl: './some-component.html'
})
export class SomeComponent {
  readonly formGroup = this.formBuilder.group({
    name: ['', Validators.required]
  });

  validateName(): void {
    const nameFormControl = this.formGroup.get('name')!;
    const { errors, value } = nameFormControl;

    if (errors || !value) return;

    return this.nameService.verifyName(value).pipe(
      take(1),
      catchError(() => just(null)),
      tap(exists => nameFormControl.setErrors(exists))
    )
    .subscribe();
  }
}

请注意,由于您没有在问题中包含 nameServive.verifyName 的签名,我假设它 returns Observable<{ exists: boolean } | null>.