myHotTake

Tag: Angular async forms

  • How Does Async Validation Work in Angular Forms?

    If this story clicks with you, feel free to like or share it—I’d love to help more folks learn in a fun way!


    I’m training for a marathon, and my coach is my validation system. Before I head out on a long run, I ask my coach, “Am I ready for this?” He takes a quick look at my shoes, checks my water bottle, and says, “Yep, all set!” That’s synchronous validation: a fast, simple check done immediately.

    But here’s the thing—I also need to know if I’m healthy enough to run today. That’s where it gets tricky. My coach can’t just glance at me and decide; he needs to send me to the doctor for a blood test. It takes some time for the results to come back. While I wait, I can stretch, warm up, or do something else productive, but I can’t start running until the results confirm I’m good to go. This is asynchronous validation.

    In Angular, asynchronous validation works the same way. For example, if I’m filling out a form with an email field, there might be a rule to check whether the email is already taken. This requires reaching out to a server—a process that doesn’t happen instantly. Angular lets me define Async Validators that return a Promise or an Observable. While the request is processing, the form stays responsive. When the server finally says, “This email is free,” or “Sorry, it’s taken,” the validation status updates.

    To set it up, I use the asyncValidators option when building my form controls. In the analogy, it’s like giving my coach the power to send me to the doctor whenever a deeper check is needed, so I can trust the process and keep things moving smoothly.


    Example: Asynchronous Validation in Angular

    Here’s how we might implement an email check with an Async Validator:

    import { AbstractControl, ValidationErrors, AsyncValidatorFn } from '@angular/forms';
    import { of } from 'rxjs';
    import { debounceTime, map, catchError, switchMap } from 'rxjs/operators';
    import { HttpClient } from '@angular/common/http';
    
    export class EmailValidator {
      static checkEmailAvailable(http: HttpClient): AsyncValidatorFn {
        return (control: AbstractControl) => {
          // Start by returning an observable
          return of(control.value).pipe(
            debounceTime(300), // Delay to simulate waiting for user to stop typing
            switchMap(email =>
              http.get<{ isAvailable: boolean }>(`https://api.example.com/check-email?email=${email}`).pipe(
                map(response => (response.isAvailable ? null : { emailTaken: true })), // Return validation error if taken
                catchError(() => of({ emailCheckFailed: true })) // Handle errors gracefully
              )
            )
          );
        };
      }
    }

    Then we attach it to a form control:

    import { FormBuilder, Validators } from '@angular/forms';
    import { HttpClient } from '@angular/common/http';
    import { EmailValidator } from './email-validator';
    
    constructor(private fb: FormBuilder, private http: HttpClient) {}
    
    form = this.fb.group({
      email: [
        '',
        [Validators.required, Validators.email], // Synchronous validators
        [EmailValidator.checkEmailAvailable(this.http)] // Asynchronous validator
      ]
    });

    Key Concepts in the Code

    1. AsyncValidatorFn: This is a function that takes a control and returns an observable or promise. It’s the heart of asynchronous validation.
    2. of and pipe: We start with the control’s value as an observable (of(control.value)) and chain RxJS operators to handle the async flow.
    3. Debouncing: Using debounceTime, we wait for the user to stop typing before sending a request—avoiding excessive server calls.
    4. Switching: switchMap ensures we only care about the latest value, canceling previous requests if the user types a new value quickly.
    5. Error Handling: With catchError, we gracefully handle any server or network issues.

    Final Thoughts & Key Takeaways

    • Asynchronous validation shines when you need to verify data externally, like with server calls.
    • It makes your app user-friendly by preventing unnecessary delays while still maintaining accurate validation.
    • Combining RxJS operators like debounceTime and switchMap ensures efficiency and responsiveness in your form controls.

    Asynchronous validation might feel complex at first, but just like a marathon, breaking it into smaller steps makes it easier to manage. And once it’s in place, your app becomes a much smoother experience for your users.