49. Handling Asynchronous Validators
Asynchronous validators are required when validation involves a backend call (e.g., checking if a username is already taken in the database). Async validators run after all synchronous validators have passed.
Async Validator Structure
An async validator function returns an Observable or Promise of ValidationErrors | null.
Example: Unique Username Validator
This validator checks the backend via an injected service.
typescript // unique-username.validator.ts import { AbstractControl, ValidationErrors, AsyncValidatorFn } from '@angular/forms'; import { Observable, timer } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; import { UserService } from './user.service';
export function uniqueUsernameValidator(userService: UserService): AsyncValidatorFn { return (control: AbstractControl): Observable<ValidationErrors | null> => { // 1. Wait 500ms before checking to reduce API calls (Debounce) return timer(500).pipe( switchMap(() => { if (!control.value) { return of(null); }
// 2. Call the service (which returns an Observable)
return userService.checkUsernameExists(control.value).pipe(
map(exists => {
// 3. Return error if user exists, else null
return exists ? { uniqueUsername: true } : null;
})
);
})
);
}; }
Applying the Async Validator
Async validators are passed as the third argument to the FormControl constructor.
typescript // In the component: constructor(private fb: FormBuilder, private userService: UserService) { this.registerForm = this.fb.group({ username: ['', [Validators.required], // Sync validators (second argument) [uniqueUsernameValidator(this.userService)] // Async validators (third argument) ] }); }
While the async validation is running, the form control's state will be pending.