Whenever it comes to building forms, I tend to go with reactive forms. In short, while using reactive forms: you create the form model consisting of different form controls directly in the component class. Then you bind the controls to actual native form elements defined in the view.
In this post, I'm going to add a custom asynchronous validator to a FormControl. For demonstration, I'm creating a simple user form. User can enter their name and email. But if the provided email already exists in the server, the user will get an error.
Following is the structure of the user component class,
Notice carefully, we already have synchronous validation constraints on both name and email i.e. the fields can't be null or empty (Validators.required) and additionally, the email should be in a valid format (Validators.email). Another validation constraint on the email is an asynchronous one, that checks for the uniqueness of the provided email on the server. You specified the synchronous validators in an array and pass it as the second parameter to the FormControl. Similarly, you specified your asynchronous validators and pass them as the third parameter to the control.
You don't have to use arrays if you have a single synchronous or asynchronous validator
Angular doesn't fire the asynchronous validators until every synchronousvalidation is satisfied. Since an asynchronous validation is resourceful (i.e. you would call some kind of server to do the validation), you shouldn't always initiate the validation process on every change made to the form control.
In the case of asynchronous validators, we define a function that returns a function that is of type AsyncValidatorFn. Following is the structure of the AsyncValidatorFn interface,
The AsyncValidatorFn returns a promise or observable. We process the validation result and decide whether it satisfies a specific validation constraint or not by resolving the promise or the observable object. Following is the validator function that checks the uniqueness of the email available in the control.value:
AbstractControl is the base class of every FormControl.
In the else statement, subscription to the getByEmail(email: string) is fired when the control is not in a debounced state (For drastically minimizing the http calls). Observable that is returned from the getByEmail(email: string) is mapped and here we check whether we have a unique email or not. If yes, we return a validation key i.e. existingEmail with the value set to the form control's value { value: control.value }. And if not, we return a null object.
The getByEmail(email: string) function is available in the user.service.ts file. We are mimicking a server call with a delay here.
HTML markup of the component is as following
Remember that we have the validation result returned with a key named existingEmail. So, we can check whether the value of the key is null or not in a conditional *ngIf directive and toggle a relevant message.
That is how easy to make custom asynchronous validators. I hope you like the post. Share it and have a nice day ☺.