Using Async Validator in FormGroup in Angular 19

February 24, 2025
In this article, I will discuss how to create and use async validators in reactive form in our Angular application.
1. Angular provides AsyncValidatorFn function that handles async validation. It receives a control and returns Promise or Observable that emits validation error if present otherwise null.
import { AsyncValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms';
export function existingUserNameValidator(service: CustomerService): AsyncValidatorFn {
    return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
      ------
    };
} 
2. Find the constructor FormControl.
constructor(formState: any = null, 
              validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null, 
              asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null) 
We can see that second argument is for sync validators and third argument is for async validators.
3. Find the code to create FormGroup that is configuring async validator.
this.customerForm = new FormGroup({
  username: new FormControl(
        '', 
        [Validators.required],
        [existingUserNameValidator(this.customerService)]
  ),
  ------
}); 
Now I will provide complete example to create async validators and configure them in our FormGroup. I am creating my example using Angular 19.

1. Create Async Validator Function using AsyncValidatorFn

username-exists-validator.ts
import { AsyncValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms';
import { Observable } from "rxjs";
import { map } from "rxjs/operators";
import { CustomerService } from './customer.service';

export function existingUserNameValidator(service: CustomerService): AsyncValidatorFn {
    return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
        return service.userNameExists(control.value).pipe(map(
            users => {
                return users.length > 0 ? { "userNameExists": true } : null;
            }
        ));
    };
} 
existing-mobile-number-validator.ts
import { AsyncValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms';
import { Observable } from "rxjs";
import { map } from "rxjs/operators";
import { CustomerService } from './customer.service';

export function existingMobNumValidator(service: CustomerService): AsyncValidatorFn {
    return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
        return service.mobNumExists(control.value).pipe(map(
            isAvailable => {
                return isAvailable ? { "mobNumExists": true } : null;
            }
        ));
    };
} 

2. Create Service

customer.service.ts
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';

@Injectable({
    providedIn: 'root'
})
export class CustomerService {
    userNameExists(username: string): Observable<string[]> {
        const users = ['aaaaa', 'bbbbb'];
        const arr = users.filter(u => u === username);
        return of(arr);
    }   
    mobNumExists(mobileNumber: string): Observable<boolean> {
        const mobNums = ['1111111', '2222222']
        const index = mobNums.findIndex(n => n === mobileNumber)
        return of(index < 0 ? false: true);
    }
} 

3. Create Component

customer.component.ts
import { CommonModule } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { FormGroup, Validators, ReactiveFormsModule, FormControl } from '@angular/forms';
import { existingMobNumValidator } from './existing-mobile-number-validator';
import { CustomerService } from './customer.service';
import { existingUserNameValidator } from './username-exists-validator';

@Component({
  selector: 'my-app',
  imports: [ReactiveFormsModule, CommonModule],
  templateUrl: './customer.component.html',
})
export class CustomerComponent implements OnInit {
  customerForm!: FormGroup;
  constructor(private customerService: CustomerService) { }
  ngOnInit() {
    this.customerForm = new FormGroup({
      username: new FormControl(
        '',
        [Validators.required, Validators.maxLength(7)],
        [existingUserNameValidator(this.customerService)]
      ),
      mobNum: new FormControl('', [Validators.required], [existingMobNumValidator(this.customerService)]),
    });
  }
  onFormSubmit() {
    console.log(this.customerForm.value);
    this.customerForm.reset();
  }
  get username() {
    return this.customerForm.get('username') as FormControl;
  }
  get mobNum() {
    return this.customerForm.get('mobNum') as FormControl;
  }
} 

4. HTML Template

customer.component.html
<form [formGroup]="customerForm" (ngSubmit)="onFormSubmit()">
	<p>Username: <input formControlName="username">
		@if(username.errors?.['required']) {
			<div style="color: red;">Username required. </div>
		 }
		 @if(username.errors?.['maxlength']) {
			<div style="color: red;">Max length is 7. </div>
		 }
		 @if(username.errors?.['userNameExists']) {
			<div style="color: red;">Username already exists. </div>
		 }
	</p>
	<p>Mobile: <input formControlName="mobNum">
	@if(mobNum.errors?.['required']) {
	   <div style="color: red;">Mobile number required. </div>
	}
	@if(mobNum.errors?.['mobNumExists']) {
	   <div style="color: red;">Mobile number already exists. </div>
	}
	</p>
	<p><button>Submit</button></p>
</form>

5. Output

Using Async Validator in FormGroup in Angular

6. Reference





©2025 tadstack.com | Privacy Policy | Contact Us