I created a standard registration form using Angular and implemented an async directive (UserExistsDirective) to validate if the email is already taken. To manage error messages, I also utilized a second directive (ValidityStyleDirective) which is also used on other input fields.
The challenge I'm facing is ensuring that ValidityStyleDirective waits for UserExistsDirective to complete before executing. Otherwise, the error message may be out of sync. I've included the code I have so far below and would appreciate any guidance on how to ensure that UserExistsDirective completes before triggering ValidityStyleDirective in Angular.
Thank you,
HTML :
...
<div class="mb-4 inputcontainer">
<label class="form-label" for="email">Email</label>
<input appUserExists validityStyle formControlName="email" class="form-control" id="email" type="email" name="email" >
<div *ngIf="registerForm.get('email')?.pending" class="icon-container">
<i class="loader"></i>
</div>
<div class="valid-feedback">Looks good!</div>
<div class="invalid-feedback">
<span *ngIf="registerForm.get('email')?.hasError('required')">Please provide an email.</span>
<span *ngIf="registerForm.get('email')?.errors?.['pattern']">Please provide a valid email.</span>
<span *ngIf="registerForm.get('email')?.errors?.['userExists']">This email address is already registered. Please use another one.</span>
</div>
</div>
...
UserExistsDirective : it add a the key userExists to validator if an email is found.
@Directive({
selector: '[appUserExists]',
providers: [{
provide: NG_ASYNC_VALIDATORS,
useExisting: UserExistsDirective,
multi: true
}]
})
export class UserExistsDirective implements AsyncValidator {
constructor() {
}
validate(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
return this.emailExists(control.value).pipe(
take(1),
map(isTaken => (isTaken ? { userExists: true } : null)),
catchError(() => of(null))
);
}
emailExists(email: string): Observable<boolean> {
return of(email).pipe(
delay(500),
map((email) => {
const emails = ['example@example.com', 'test@test.com'];
return emails.includes(email);
})
);
}
}
Then ValidityStyleDirective adds the necessary error styling when needed
@Directive({
selector: '[validityStyle]'
})
export class ValidityStyleDirective {
constructor(@Self() private ngControl: NgControl,
private formGroup: FormGroupDirective,
private renderer: Renderer2,
private el: ElementRef) { }
@HostListener('blur')
onBlur($event: any) {
if (this.ngControl.errors == null) {
this.formGroup.control.get('email')?.setErrors(null);
}
if (this.ngControl.value === '') {
this.renderer.removeClass(this.el.nativeElement, 'is-invalid');
return false;
}
if (!this.ngControl.valid) {
this.renderer.addClass(this.el.nativeElement, 'is-invalid');
return true;
}
this.renderer.removeClass(this.el.nativeElement, 'is-invalid');
return false;
}
}