Back to course

Handling Asynchronous Validators

The Complete Angular Developer: From Zero to Hero

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.