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

时间:2018-11-07 15:50:20

标签: angular angular-reactive-forms reactive-forms angular7

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

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;
            })
        );
    }
}

到目前为止一切正常。当我插入现有电子邮件时,会收到错误消息“此电子邮件已被使用”。

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

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不再显示错误消息“此电子邮件已在使用中”。错误代码“ uniqueEmail”未添加到控件的错误对象中。

有人可以阐明这一点吗? 预先感谢。

1 个答案:

答案 0 :(得分:1)

我需要完全相同的东西。所以我做的没什么不同。我创建了一个异步验证方法,该方法会在发生错误的情况下返回验证错误的保证。因此,此方法将对服务器的呼叫延迟500毫秒,并且如果在延迟期间发生任何更改,它将取消前一个超时并开始新的超时。因此,如果用户正在键入,则最终只会在服务器上发送一个呼叫。在下面看看。

声明这两个变量

validationDelay: any;
validateUniqueEmail: any;

定义以下异步验证方法。记住,我使用实例var是因为我不能在promise中使用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);
 };
}

在您的validateUniqueEmail中使用此FormControl

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