How To Create a Custom Validator for Reactive Forms in Angular
Table of Contents
Introduction #
Angular’s @angular/forms
package comes with a Validators
class that supports useful built-in validators like required
, minLength
, maxLength
, and pattern
. However, there may be form fields that require more complex or custom rules for validation. In those situations, you can use a custom validator.
When using Reactive Forms in Angular, you define custom validators with functions. If the validator does not need to be reused, it can exist as a function in a component file directly. Otherwise, if the validator needs to be reused in other components, it can exist in a separate file.
In this tutorial, you will construct a reactive form with a reusable custom validator to check if a URL meets certain conditions.
Prerequisites #
To complete this tutorial, you will need:
Node.js installed locally, which you can do by following How to Install Node.js and Create a Local Development Environment.
Some familiarity with setting up an Angular project.
This tutorial was verified with Node v15.2.1, npm
v6.14.8, @angular/core
v11.0.0, and @angular/forms
v11.0.0.
Step 1 – Setting Up the Project #
For the purpose of this tutorial, you will build from a default Angular project generated with @angular/cli
.
npx @angular/cli new angular-reactive-forms-custom-validtor-example --style=css --routing=false --skip-tests
This will configure a new Angular project with styles set to “CSS” (as opposed to “Sass”, Less”, or “Stylus”), no routing, and skipping tests.
Navigate to the newly created project directory:
cd angular-reactive-forms-custom-validator-example
To work with reactive forms, you will be using the ReactiveFormsModule
instead of the FormsModule
.
Open app.module.ts
in your code editor amd add ReactiveFormsModule
:
src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
ReactiveFormsModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
At this point, you should have a new Angular project with ReactiveFormsModule
.
Step 2 – Building a Custom Validator #
The example custom validator for this tutorial will take a URL string and ensure that it starts with the https
protocol and ends with the .io
top-level domain.
First, in your terminal, create a shared
directory:
mkdir src/shared
Then, in this new directory, create a new url.validator.ts
file. Open this file in your code editor and add the following lines of code:
src/shared/url.validator.ts
import { AbstractControl } from '@angular/forms';
export function ValidateUrl(control: AbstractControl) {
if (!control.value.startsWith('https') || !control.value.includes('.io')) {
return { invalidUrl: true };
}
return null;
}
This code uses the Notice AbstractControl
class, which is the base class for FormControl
s, FormGroup
s, and FormArray
s. This allows access to the value of the FormControl
.
This code will check to see if the value startsWith
the string of characters for https
. It will also check to see if the value includes
the string of characters for .io
.
If the validation fails, it will return an object with a key for the error name, invalidUrl
, and a value of true
.
Otherwise, if the validation passes, it will return null
.
At this point, your custom validator is ready for use.
Step 3 – Using the Custom Validator #
Next, create a form that takes a userName
and a websiteUrl
.
Open app.component.ts
and replace the content with the following lines of code:
src/app/app.component.ts
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ValidateUrl } from '../shared/url.validator';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
myForm: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.myForm = this.fb.group({
userName: ['', Validators.required],
websiteUrl: ['', [Validators.required, ValidateUrl]]
});
}
saveForm(form: FormGroup) {
console.log('Valid?', form.valid); // true or false
console.log('Username', form.value.userName);
console.log('Website URL', form.value.websiteUrl);
}
}
In this code, the websiteUrl
form control uses both the built-in Validators.required
and the custom ValidateUrl
validator.
Step 4 – Accessing the Errors in the Template #
Users interacting with your form will need feedback on what values are failing validation. In the component template, you can use the key you defined in the return value of a custom validator.
Open app.component.html
and replace the content with the following lines of code:
src/app/app.component.html
<form [formGroup]="myForm" ngSubmit)="saveForm(myForm)">
<div>
<label>
Username:
<input formControlName="userName" placeholder="Your username">
</label>
<div *ngIf="(
myForm.get('userName').dirty ||
myForm.get('userName').touched
) &&
myForm.get('userName').invalid"
>
Please provide your username.
</div>
</div>
<div>
<label>
Website URL:
<input formControlName="websiteUrl" placeholder="Your website">
</label>
<div
*ngIf="(
myForm.get('websiteUrl').dirty ||
myForm.get('websiteUrl').touched
) &&
myForm.get('websiteUrl').invalid"
>
Only URLs served over HTTPS and from the .io top-level domain are accepted.
</div>
</div>
</form>
At this point, you can compile your application:
npm start
And open it in your web browser. You can interact with the fields for userName
and websiteUrl
. Ensure that your custom validator for ValidateUrl
works as expected with a value that should satisfy the conditions of https
and .io
: https://example.io
.
Conclusion #
In this article, you created a reusable custom validator for a reactive form in an Angular application.
For examples of custom validators in template-driven forms and reactive forms, consult Custom Form Validation in Angular.
If you’d like to learn more about Angular, check out our Angular topic page for exercises and programming projects.