Adding async Validators to your Reactive form in Angular

Adding async Validators to your Reactive form in Angular

Learn how you can add async validators to your reactive form in Angular.

Occasionally, you may want to validate form input against data that is available asynchronous source i.e. a HTTP backend. For instance, checking if a username or email address exists before form submission. In Angular, you achieve this using Async Validators, which we are going to look at in this post.

We are going to build a simple reactive form. It will contain a single form field called username. Then, whenever the user enters their username, we are going to check if it exists and return the response. If the username exists, then the form shall report the following error – The username is already taken., as shown below.

Prerequisite

I am going to skip the process of setting up a new Angular Project. Once you have your Angular project setup, in your app module, you will need to import ReactiveFormsModule from @angular/forms. This is the only module we require to use Reactive Forms inside our angular application.

@NgModule({
  // ...
  imports: [
   BrowserModule,
   ReactiveFormsModule
  ],
  // ...
})

export class AppModule {}

Next, lets add a method for looking up username methods.

Username Lookup Method

First, we are going to add a method to check whether the user’s username exists. In a normal application, this is where you would make a HTTP request to your backend API – either a REST, a GraphQL API etc. But for the purpose of this post, we are going to simulate a HTTP request using RXJS Delay Operator. This will delay our responses by about one second and return an observable just like a HTTP request in Angular.

To achieve this, we are going to create an array of taken usernames - takenUsernames.

takenUsernames = [
  'hello',
  'world',
  'username'
  // ...
];

Then given a username, we are going to determine if it exists within the array using array includes method. After that, whatever the results, we will then delay the response for about a second and return the results as a boolean Observable – Observables<boolean>.

checkIfUsernameExists(username: string): Observable<boolean> {
  return of(this.takenUsernames.includes(username)).pipe(delay(1000));
}

Adding an Async Validator

Next up, we are going to create our async validator. To create this, we just need to implement the <a href="https://angular.io/api/forms/AsyncValidatorFn" target="_blank">AsyncValidatorFn</a> interface. To implement the AsyncValidatorFN interface, you need a method that receives a form control class (AKA <a href="https://angular.io/api/forms/AbstractControl" target="_blank">AbstractControl</a>) as a parameter. The method then needs to return a promise or an observable of ValidationErrors or null. If there are any errors, the method returns ValidationErrors, otherwise it just returns null.

A ValidationErrors is an another interface, which is just a key value map of all errors thrown, as shown below:

type ValidationErrors = {
    [key: string]: any;
};

For instance, our error for this posts demo shall look something like this { usernameExists: true }. The key of the returned error allows you to check for specific errors on your form and display them to the user. This allows you to give a more precise feedback to the user instead of generic feedback.

Please note, when there are no errors, you should always return null. If you don’t return null, your Angular Form will be in an invalid state. This could have some undesired side effects. In our method to check for errors, we will map the boolean response from checkIfUsernameExists method above to a ValidationErrors or null response. It will return null if false and a ValidationErrors object if true.

usernameValidator(): AsyncValidatorFn {
  return (control: AbstractControl): Observable<ValidationErrors | null> => {
    return this.checkIfUsernameExists(control.value).pipe(
      map(res => {
        // if res is true, username exists, return true
        return res ? { usernameExists: true } : null;
        // NB: Return null if there is no error
      })
    );
  };
}

We will tack the above method inside our username lookup service.

Adding Async Validators to Our Reactive Form

Now that we have a full working service, we need to use the async validator we just created above. This is a simple matter of adding the array of async validators, after synchronous validators, as shown below.

return this.fb.group({
  username: [
    null, [Validators.required],  [this.usernameService.usernameValidator()]
  ]
});

Remember to inject the usernameLookupService into the component you are using it in.

constructor(private fb: FormBuilder, private usernameService: UsernameValidationService) {
  // ...
}

UI Implementation

And finally, inside your template, you can check if the form has errors. You can check if a form field has an validation error inside the template as shown below.

frmAsyncValidator.controls['username'].errors?.usernameExists

This returns true if there is an error, else it is undefined, hence the use of <a href="https://angular.io/guide/template-syntax#safe-navigation-operator" target="_blank">safe navigation operator (?)</a>. And here is the full template for our form:

<form [formGroup]="frmAsyncValidator">
  <div class="field has-text-left">
    <label class="label">Username </label>
    <div [ngClass]="{'is-loading':
frmAsyncValidator.controls['username'].pending }" class="control">
      <input [ngClass]="{'is-danger': hasError('username', 'any')}" formControlName="username" class="input is-focused" type="username" placeholder="Your username">
    </div>
    <div class="has-text-danger" *ngIf="frmAsyncValidator.controls['username'].errors?.usernameExists">
      This username is already taken!
    </div>
  </div>
</form>

A Note on Performance

Currently as we have implemented our async validator, it runs on form value changes. For sync validators, you will likely never notice any performance impact. For async validators however, this can have some undesired side effects. This is because you will most likely be sending HTTP requests to some sort of backend for validation. Running validation on form value changes can end up straining the backend with too many requests.

For this reason, you can change your validators to run onBlur, instead of on form value changes. You can achieve this by adding the updateOn property to { updateOn: 'blur' } for either an individual form control or the entire form:

This is how you can set { updateOn: 'blur' } property for an entire form:

this.fb.group({/** field here */}, { updateOn: 'blur' });

And this is how you do it for an individual form control:

username: [
  null,
  {
    validators: [Validators.required],
    asyncValidators: [this.usernameService.usernameValidator()],
    updateOn: 'blur'
  }
]

Conclusion

That’s it for me in this post. If you have any questions/issue/suggestion, feel free to use the comment section below.

If you liked this post, share it with all of your programming buddies! :)

react-native angular angular-js reactjs web-development

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

How native is React Native? | React Native vs Native App Development

Article covers: How native is react native?, React Native vs (Ionic, Cordova), Similarities and difference between React Native and Native App Development.

Hire Dedicated React Native Developer

Have you ever thought of having your own app that runs smoothly over multiple platforms? React Native is an open-source cross-platform mobile application framework which is a great option to create mobile apps for both Android and iOS. **[Hire...

Hire Dedicated React Native Developer in India | React Native Development

Hire dedicated React Native developers for your next project. As the top react native development company we offer you the best service as per your business needs.

Hire React Js Developer from Expert React JS Development Company

Are you looking to [hire React JS developer](https://www.mobiwebtech.com/react-js-development-company/ "hire React JS developer") from a prestigious and reliable React JS development company? Visit at Mobiweb Technologies here we have a big team...

A Short Guide to React Native App Development

React Native is undoubtedly one of the most widely used cross-platform frameworks for creating native-like apps. This framework can be easily used for developing brand-new apps from scratch and even in existing iOS or Android projects.easily used for developing brand-new apps from scratch and even in existing iOS or Android projects.