Learn how to build a custom validator in Angular Reactive form. A data entry form can contain a large no of fields. The Angular forms module makes it easier to create, manage, and validate the form fields. There are two ways in which you can create forms in Angular. One is Reactive forms & the other one is template-driven forms. Learn how to create reactive forms & how to create template-driven forms.
Table of Contents
Built-in Validators
Validating the Forms is very important, otherwise, we will end up having invalid data in our database. The Angular Forms Module provides a few built-in validators to help us to validate the form. They are listed below.
We covered them in the validation in reactive forms & validation in template-driven forms tutorial.
Custom Validator in Angular Reactive Form
Built-in validators are useful but do not cover all use cases. This is where we use the custom validator. It is very easy to create a custom validator in Angular.
How to Build Custom Validator
Building a custom Validator is as easy as creating a Validator
function. It is a function, which must implement ValidatorFn Interface.
ValidatorFn
The ValidatorFn
is an Interface, which defines the signature of the Validator function.
1 2 3 4 5 | interface ValidatorFn { (control: AbstractControl): ValidationErrors | null } |
The function takes the AbstractControl. This is the base class for FormControl
, FormGroup
, and FormArray
. The validator function must return a list of errors i.e ValidationErrors or null
if the validation has passed
Custom Validator Example
Create a new angular application. Add the following code in app.component.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <h1>Custom Validator in Angular</h1> <h2>Reactive Form</h2> <form [formGroup]="myForm" (ngSubmit)="onSubmit()" novalidate> <div> <label for="numVal">Number :</label> <input type="text" id="numVal" name="numVal" formControlName="numVal"> </div> <p>Is Form Valid : {{myForm.valid}} </p> <p> <button type="submit" [disabled]="!myForm.valid">Submit</button> </p> </form> |
Our example app has numVal
form field. We want it to be greater than 10.
Angular does not have any built-in validator for that. Hence let us build a custom Validator gte
Create a new file gte.validator.ts
under the app
folder.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import { AbstractControl, ValidationErrors } from '@angular/forms' export function gte(control: AbstractControl): ValidationErrors | null { const v=+control.value; if (isNaN(v)) { return { 'gte': true, 'requiredValue': 10 } } if (v <= 10) { return { 'gte': true, 'requiredValue': 10 } } return null } |
First, import the AbstractControl
and ValidationErrors
from the @angular/forms
1 2 3 | import { AbstractControl, ValidationErrors } from '@angular/forms' |
The validator function must adhere to the ValidatorFn Interface. It should receive the AbstractControl
as its parameter. It can be FormControl
, FormGroup
or FormArray
.
The function must validate the control value and return ValidationErrors
if any errors are found otherwise null
.
1 2 3 | export function gte(control: AbstractControl): ValidationErrors | null { |
The ValidationErrors
is a key-value pair object of type [key: string]: any
and it defines the broken rule. The key
is the string and should contain the name of the broken rule. The value can be anything, but usually set to true
.
The validation logic is very simple. Check if the value of the control is a number using the isNaN method. Also, check if the value is less than or equal to 10. If both the rules are valid and then return null
1 2 3 4 5 6 7 8 9 10 11 12 13 | const v=+control.value; if (isNaN(v)) { return { 'gte': true, 'requiredValue': 10 } } if (v <= 10) { return { 'gte': true, 'requiredValue': 10 } } return null |
If the validation fails then return the ValidationErrors
. You can use anything for the key
, but it is advisable to use the name of the validator i.e gte
as the key. Also, assign true
as value. You can as well assign a string value.
1 2 3 | return { 'gte': true, 'requiredValue': 10 } |
You can return more than one key-value
pair as shown in the above example. The second key requiredValue
returns the value 10. We use this in the template to show the error message.
Using the Custom Validator
To use this validator first, import it in the component class.
1 2 3 | import { gte } from './gte.validator'; |
Add the validator to the Validator collection of the FormControl
as shown below.
1 2 3 4 5 6 | myForm = new FormGroup({ numVal: new FormControl('', [gte]), }) |
The complete app.component.ts
is shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | import { Component } from '@angular/core'; import { FormGroup, FormControl, AbstractControl, ValidationErrors } from '@angular/forms' import { gte } from './gte.validator'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { constructor() { } myForm = new FormGroup({ numVal: new FormControl('', [gte]), }) get numVal() { return this.myForm.get('numVal'); } onSubmit() { console.log(this.myForm.value); } } |
Accessing the Errors from Custom Validator
We need to provide a meaningful error message to the user.
Validators return ValidationErrors
. They are added to the control’s errors
collection of the control. The valid
property of the control is set to false
.
Hence we check if the valid
property. We also check the dirty
and touched
property. Because we do not want to display the error message when the form is displayed for the first time.
1 2 3 4 5 6 7 8 9 10 11 12 | <div> <label for="numVal">Number :</label> <input type="text" id="numVal" name="numVal" formControlName="numVal"> <div *ngIf="!numVal.valid && (numVal.dirty ||numVal.touched)"> <div *ngIf="numVal.errors.gte"> The number should be greater than {{numVal.errors.requiredValue}} </div> </div> </div> |
We check if the gte
is true and display the error message. Note that gte
is the name of the key
we used while creating the validator.
We also make use of the requiredValue
to show a meaningful message to the user.
1 2 3 4 5 | <div *ngIf="numVal.errors.gte"> The number should be greater than {{numVal.errors.requiredValue}} </div> |
Now, we have successfully built a custom validator in reactive forms.
Next Steps
Our Custom validator is simple, but it can be improved.
Instead of the hardcoded value of 10, we want it set it while defining the validator. i.e we want to send the parameter to the validator function.
The validator might depend on a service to be injected.
You can refer to the following tutorials on Angular Forms
- Angular Forms Tutorial: Fundamental & Concepts
- Template Driven Forms in Angular
- Set Value in Template Driven forms in Angular
- Reactive Forms in Angular
- FormBuilder in Reactive Forms
- SetValue & PatchValue in Angular
- StatusChanges in Angular Forms
- ValueChanges in Angular Forms
- FormControl
- FormGroup
- FormArray Example
- Build Dynamic or Nested Forms using FormArray
- Validations in Reactive Forms in Angular
- Custom Validator in Reactive Forms
- Passing Parameter to Custom Validator in Reactive Forms
- Inject Service into Custom Validator
- Validation in Template Driven Forms
- Custom Validator in Template Driven Forms
Summary
The Validators are nothing but function, which implements the ValidatorFn Interface. They receive an instance of Form Control. It checks the control value for validity. If the validation errors are present, then they must return ValidationErrors. Return null if the validation is successful.
One thing that makes any tutorial unclear to someone learning a topic is when the author sets many variables and labels to the same value when they don’t have to be the same value. For example your code has this line
…
The number should be greater than {{numVal.errors.requiredValue}}
Sooo.. is numVal.errors from the id, the name, or the formControlName on the input element???? You set them all the same. Do they have to be the same? Which one does ____.errors have to match?
This would all be more clear if you only made values the same if they MUST be the same, e.g.
I am geting this error “Unresolved variable gte ” in html page … Please help
Check the code
https://stackblitz.com/edit/angular-custom-validator-in-angular-reactive-form-1
URGENT!!
I am getting an Error in app.component.html
Can’t bind to ‘formGroup’ since it isn’t a known property of ‘form’.
Check it out
https://stackoverflow.com/questions/39152071/cant-bind-to-formgroup-since-it-isnt-a-known-property-of-form