I've written an async validator to check if the entered email already exists in my database.
I use the reactive form api to build my form and I've configure my async validator to only trigger on "blur". As mentionned in the angular documentation
it works well: the validation is triggered when I leave the field BUT the error message is not shown until I interact with my form.
If I trigger the changeDetection manually with a setTimeout function in my validation, it works.
Any idea why this error is not shown directly after the validation completes?
Here is my form definition:
private initPersonalInformationFormGroup() {
this.personalInformationFormGroup = this._formBuilder.group({
lastName: ['', Validators.required],
firstName: ['', Validators.required],
gender: [Gender.MALE],
birthDate: [''],
birthPlace: [''],
nationality: [''],
inss: ['', [Validators.minLength(11), Validators.maxLength(11), Validators.pattern('[0-9]{11}')]],
email: ['', {
validators: [Validators.required, Validators.email],
asyncValidators: [this._studentEmailValidator()],
updateOn: 'blur'
}],
phoneNumber: [null, Validators.pattern('\\+32[1-9][0-9]{7,8}')],
address: this._formBuilder.group({
street: ['', [Validators.maxLength(60)]],
houseNumber: ['', [Validators.maxLength(10)]],
city: ['', [Validators.maxLength(60)]],
postalCode: [null, [Validators.min(1000), Validators.max(9999)]],
}, {
validators: this._completeAddressValidator()
}),
previousSchool: ['', Validators.maxLength(60)],
additionalInformation: ['']
})
}
And my validation method :
private _studentEmailValidator(): AsyncValidatorFn {
return (control: FormGroup): Observable<{ [key: string]: any } | null> => {
const email = control.value;
// setTimeout(() => this._checkDetectorRef.detectChanges(), 5000);
return this._adminFacade.checkStudentWithEmailExists(email).pipe(
take(1),
map(exists => exists ? {'emailAlreadyUserByStudent': {value: email}} : null),
catchError(() => null)
);
}
};
and the part of the template :
<mat-form-field fxFlex="60">
<mat-placeholder>
<mat-icon>email</mat-icon>
<span> Email</span>
</mat-placeholder>
<input matInput formControlName="email">
<mat-error *ngIf="email.hasError('emailAlreadyUserByStudent')">
Email déjà utilisé.
</mat-error>
<span *ngIf="email.pending">Validating...</span>
</mat-form-field>
As a workaround (I'm still not sure it's the best way to do it because it's not necessary in the code examples..), I've added a manual detectChanges at the end of my validator function :
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))
);
}
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With