Tutorial

How To Create a Custom Validator for Reactive Forms in Angular

Updated on March 19, 2021
author

Alligator.io

How To Create a Custom Validator for Reactive Forms in Angular

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:

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.

  1. 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:

  1. 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:

  1. 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 FormControls, FormGroups, and FormArrays. 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:

  1. 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.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products


About the authors
Default avatar
Alligator.io

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
1 Comments


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Great tutorial! Thanks for sharing.

But what if I need to pass extra params to the Validator?

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Featured on Community

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel