添加计时器后 AbstractControl 不包含异步验证错误

AbstractControl doesn't contain async validation error after adding a timer

我正在创建一个带有反应式表单的注册表单,客户必须在其中插入电子邮件地址。我向此控件添加了一个异步验证器,以检查插入的电子邮件是否已在使用中。

this.form = this.formBuilder.group({
  ...,
  emailGroup: this.formBuilder.group({
    email: ['', [Validators.required, Validators.email], uniqueEmailValidator(this.registerService)],
    confirmEmail: ['', [Validators.required]]
  }, { validator: emailMatcher }),
  ...
});

我的 html 看起来像这样:

<div class="col-sm-6">
  <div class="form-group">
    <label for="reg-email">E-mailadres</label>
    <input class="form-control" type="text" id="reg-email" formControlName="email" [ngClass]="{'is-invalid': form.get('emailGroup.email').errors && (form.get('emailGroup.email').touched || isSubmitted)}">
    <div class="invalid-feedback">
      <div *ngIf="form.get('emailGroup.email').errors?.required || form.get('emailGroup.email').errors?.email">
        Please insert a valid email
      </div>
      <div *ngIf="form.get('emailGroup.email').errors?.uniqueEmail">                
        This email is already in use
      </div>
    </div>
  </div>
</div>

原来我的异步验证器是这样的:

export function uniqueEmailValidator(registerService: RegisterService): AsyncValidatorFn {
    return (c: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
        return registerService.getCustomersByEmail(c.value).pipe(
            map(customers => {
                return customers && customers.length > 0 ? { 'uniqueEmail': true } : null;
            })
        );
    }
}

到目前为止一切正常。当我插入现有电子邮件时,我收到错误消息 'this email is already in use'。

我注意到这个异步验证器是针对每个插入的字符执行的,出于性能原因,我想避免这种情况。我决定添加一个 500 毫秒的计时器,并且仅在 500 毫秒没有 activity 时才执行异步验证。这将我的异步验证器更改为:

export function uniqueEmailValidator(registerService: RegisterService): AsyncValidatorFn {
    return (c: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
        return timer(500).pipe(
            map(x => {
                return registerService.getCustomersByEmail(c.value).pipe(
                    map(customers => {
                        return customers && customers.length > 0 ? { 'uniqueEmail': true } : null;
                    })
                );
            })
        );
    };
}

验证器正确地完成了工作,并且电子邮件控件在异步验证后被标记为无效。问题是我的 html 不再显示错误消息 'This email is already in use'。错误代码 'uniqueEmail' 没有添加到我的控件的错误对象中。

有人可以阐明这一点吗? 提前致谢。

我需要完全相同的东西。所以我做的有点不同。我创建了一个异步验证方法,该方法 returns 在出现错误时承诺验证错误。因此,此方法将对服务器的调用延迟 500 毫秒,如果在延迟期间有任何变化,它会取消先前的超时并启动新的超时。因此,如果用户正在输入,最终只会在服务器上发送一次调用。往下看

声明这两个变量

validationDelay: any;
validateUniqueEmail: any;

定义以下异步验证方法。请记住,我使用实例变量是因为我无法在承诺中使用 this 上下文。所以不要混淆。

validateEmail( c: FormControl): Promise<{[key: string]: any}> {
const instance = this;
return new Promise(resolve => {
  if (instance.validationDelay) {
    clearTimeout(instance.validationDelay);
  }
  instance.validationDelay = setTimeout(() => {
   instance.registerService.getCustomersByEmail(c.value)
    .subscribe((customers) => {
      if (customers && customers.length > 0) {
        resolve({
          uniqueEmail: true
        });
      } else {
        resolve(null);
      }
    }, (err) => {
      resolve(null);
    });
  }, 500);
});
}

现在使用这个异步方法。初始化方式如下

ngOnInit(): void {
 this.validateUniqueEmail = (control: FormControl) => {
  return this.validateEmail(control);
 };
}

在您的 FormControl

中使用此 validateUniqueEmail

email: ['', [Validators.required, Validators.email], this.validateUniqueEmail]