Angular 7:异步验证器更改检测

Sté*_*ane 7 validation asynchronous angular

我编写了一个异步验证器来检查输入的电子邮件是否已存在于我的数据库中。

\n\n

我使用反应式表单 api 来构建我的表单,并将异步验证器配置为仅在“模糊”时触发。正如角度文档中提到的

\n\n

它运行良好:当我离开该字段时会触发验证,但直到我与表单交互时才会显示错误消息。

\n\n

如果我在验证中使用 setTimeout 函数手动触发changeDetection,它就会起作用。

\n\n

知道为什么验证完成后不直接显示此错误吗?

\n\n

这是我的表单定义:

\n\n
private initPersonalInformationFormGroup() {\n  this.personalInformationFormGroup = this._formBuilder.group({\n    lastName: [\'\', Validators.required],\n    firstName: [\'\', Validators.required],\n    gender: [Gender.MALE],\n    birthDate: [\'\'],\n    birthPlace: [\'\'],\n    nationality: [\'\'],\n    inss: [\'\', [Validators.minLength(11), Validators.maxLength(11), Validators.pattern(\'[0-9]{11}\')]],\n    email: [\'\', {\n      validators: [Validators.required, Validators.email],\n      asyncValidators: [this._studentEmailValidator()],\n      updateOn: \'blur\'\n    }],\n    phoneNumber: [null, Validators.pattern(\'\\\\+32[1-9][0-9]{7,8}\')],\n    address: this._formBuilder.group({\n      street: [\'\', [Validators.maxLength(60)]],\n      houseNumber: [\'\', [Validators.maxLength(10)]],\n      city: [\'\', [Validators.maxLength(60)]],\n      postalCode: [null, [Validators.min(1000), Validators.max(9999)]],\n    }, {\n      validators: this._completeAddressValidator()\n    }),\n    previousSchool: [\'\', Validators.maxLength(60)],\n    additionalInformation: [\'\']\n  })\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我的验证方法:

\n\n
private _studentEmailValidator(): AsyncValidatorFn {\n  return (control: FormGroup): Observable<{ [key: string]: any } | null> => {\n    const email = control.value;\n    // setTimeout(() => this._checkDetectorRef.detectChanges(), 5000);\n    return this._adminFacade.checkStudentWithEmailExists(email).pipe(\n      take(1),\n      map(exists => exists ? {\'emailAlreadyUserByStudent\': {value: email}} : null),\n      catchError(() => null)\n    );\n  }\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n

以及模板的部分:

\n\n
<mat-form-field fxFlex="60">\n  <mat-placeholder>\n    <mat-icon>email</mat-icon>\n    <span> Email</span>\n  </mat-placeholder>\n  <input matInput formControlName="email">\n  <mat-error *ngIf="email.hasError(\'emailAlreadyUserByStudent\')">\n    Email d\xc3\xa9j\xc3\xa0 utilis\xc3\xa9.\n  </mat-error>\n  <span *ngIf="email.pending">Validating...</span>\n</mat-form-field>\n
Run Code Online (Sandbox Code Playgroud)\n

Sté*_*ane 5

作为一种解决方法(我仍然不确定这是最好的方法,因为在代码示例中没有必要......),我在验证器函数的末尾添加了一个手动 detectorChanges :

  private _studentEmailValidator(): AsyncValidatorFn {
return (control: FormGroup): Observable<{ [key: string]: any } | null> => {
  const email = control.value;
  return this._adminFacade.checkStudentWithEmailExists(email).pipe(
    take(1),
    map(exists => exists ? {'emailAlreadyUserByStudent': {value: email}} : null),
    catchError(() => null),
    tap(() => setTimeout(() => this._checkDetectorRef.detectChanges(), 0))
  );
}
Run Code Online (Sandbox Code Playgroud)

};

  • 这是唯一对我有用的答案,“setTimeout()”的替代方案是在“tap”语句之前添加“delay(0)”,这基本上是相同的事情,但它更 NgRx,所以更酷孩子们更喜欢它。 (2认同)

Lea*_*ima -1

尝试这样做:

import { ChangeDetectorRef } from '@angular/core';

constructor(
  changeDetectorRef: ChangeDetectorRef
) {
  //call this when you receive the backend response
  changeDetectorRef.markForCheck();
}
Run Code Online (Sandbox Code Playgroud)