使用 ngrx 进行异步验证

AsyncValidation with ngrx

我有以下表格,我想根据 ngrx 存储中的值进行验证

this.form = new FormGroup({
     serviceNr: new FormControl('', {
        validators: [Validators.required],
     }, [this.doubleServiceNrAsyncValidator()]),
     ...
});

我确保我的验证器是有限的,根据 Angular 文档

ngOnDestroy(): void {
    this.isDestroyed.next(true);
    this.isDestroyed.complete();
}

在我的模板中

<mat-error *ngIf="form.get('serviceNr').hasError('existingNr')">
        {{ 'GENERAL.ALERTS.ERROR.RESOURCE_ALREADY_EXIST' | translate : { number: this.form.get('serviceNr').value } }}
 </mat-error>

我的验证函数如下所示:

doubleServiceNrAsyncValidator = () => (_) => this.store.pipe(select(selectError)).pipe(
    takeUntil(this.isDestroyed),
    map(err => {
      if (err?.errorCode === ErrorCodeEnum.RESOURCE_ALREADY_EXIST) {
        return { existingNr: true };
      }
      if (!!err) {
        return { unknown: true };
      }
      return null;
    }),
)

正在调用异步验证器,它 returns { existingNr: true } 没问题。但是该字段未被标记为 invalid

这种行为是由于您的 this.store.pipe(select(selectError)) observable 永远不会完成,这导致与验证关联的表单控件处于 PENDING 状态。当 formControl 处于这种状态时,不会对其应用任何更改 valid 属性.

快速解决方法是在验证器中使用 take(1)first(),如下所示:

doubleServiceNrAsyncValidator = () => (_) => this.store.pipe(select(selectError)).pipe(
    first(),
    map(err => {
      if (err?.errorCode === ErrorCodeEnum.RESOURCE_ALREADY_EXIST) {
        return { existingNr: true };
      }
      if (!!err) {
        return { unknown: true };
      }
      return null;
    }),

first 运算符将完成源流,这将导致 formControl 将所有必要的更改应用到 valid 字段

在下面的 CodeSandbox 中,我创建了一个并排示例,您和将来的任何人都可能会发现它很有用,因为这是您通常不会在文档中找到的情况之一:D