首页 > 解决方案 > 使用 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;
    }),
)

正在调用异步验证器,它返回{ existingNr: true }正常。但该字段未标记为invalid

标签: angularangular-materialangular-validation

解决方案


这种行为是由于您的 observable 从未完成这一事实引起的 this.store.pipe(select(selectError)),这导致与验证相关联的表单控件处于某种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


推荐阅读