Creating Custom Validators in Angular

Tutorial 5 of 5

1. Introduction

Goal of the Tutorial

The goal of this tutorial is to teach you how to create custom validators in Angular. We'll guide you through the process of creating and implementing your own custom validators, allowing you to implement complex validation rules that aren't covered by Angular's built-in validators.

What You Will Learn

By the end of this tutorial, you will learn:
- What custom validators are in Angular
- How to create a custom validator
- How to use a custom validator in your Angular forms

Prerequisites

  • Basic understanding of Angular
  • Familiarity with TypeScript and Angular Forms

2. Step-by-Step Guide

In Angular, we can create custom validators by creating a function that takes a FormControl instance and returns an error object (if the control is invalid) or null (if the control is valid).

Creating a Custom Validator

Let's create a simple custom validator that checks if the input is a positive number.

import { AbstractControl, ValidatorFn } from '@angular/forms';

// Custom Validator Function
export function PositiveNumberValidator(): ValidatorFn {
  return (control: AbstractControl): {[key: string]: any} | null => {
    const isNotPositive = control.value <= 0;
    return isNotPositive ? {notPositive: {value: control.value}} : null;
  };
}

Here, PositiveNumberValidator is a function that returns a function (ValidatorFn). Inside the ValidatorFn, we check if the control's value is not positive. If it's not, we return an error object {notPositive: {value: control.value}}, otherwise we return null.

Using a Custom Validator

We can now use our custom validator in our form controls. Here's an example:

import { FormBuilder, Validators } from '@angular/forms';
import { PositiveNumberValidator } from './validators';

// Form Initialization
this.form = this.fb.group({
  price: ['', [Validators.required, PositiveNumberValidator()]],
});

In this example, we used the PositiveNumberValidator in the price control. So the price must be a positive number.

3. Code Examples

Here are some more practical examples:

Example 1: Email Validator

This custom validator will check if the input is a valid email address.

// Custom Email Validator
export function EmailValidator(): ValidatorFn {
  return (control: AbstractControl): {[key: string]: any} | null => {
    const validEmail = /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/.test(control.value);
    return !validEmail ? {invalidEmail: {value: control.value}} : null;
  };
}

Example 2: Password Strength Validator

This custom validator will check if the password is strong.

// Password Strength Validator
export function PasswordStrengthValidator(): ValidatorFn {
  return (control: AbstractControl): {[key: string]: any} | null => {
    const strongPassword = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$/.test(control.value);
    return !strongPassword ? {weakPassword: {value: control.value}} : null;
  };
}

4. Summary

In this tutorial, we've learned how to create and use custom validators in Angular. We've created a simple positive number validator, an email validator, and a password strength validator. The next step is to learn about async validators, which allow you to validate data against a server.

5. Practice Exercises

  1. Exercise 1: Create a custom validator that checks if a string has at least one uppercase letter, one lowercase letter, and one number.
  2. Exercise 2: Create a custom validator that checks if a number is within the range of 1 to 100.
  3. Exercise 3: Create a custom validator that checks if an input is a valid URL.

Solutions
1. Solution 1:

export function AlphaNumericValidator(): ValidatorFn {
  return (control: AbstractControl): {[key: string]: any} | null => {
    const isValid = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]+$/.test(control.value);
    return !isValid ? {invalidInput: {value: control.value}} : null;
  };
}
  1. Solution 2:
export function RangeValidator(): ValidatorFn {
  return (control: AbstractControl): {[key: string]: any} | null => {
    const isInRange = control.value >= 1 && control.value <= 100;
    return !isInRange ? {outOfRange: {value: control.value}} : null;
  };
}
  1. Solution 3:
export function UrlValidator(): ValidatorFn {
  return (control: AbstractControl): {[key: string]: any} | null => {
    const isValid = /^(http|https):\/\/[^ "]+$/.test(control.value);
    return !isValid ? {invalidUrl: {value: control.value}} : null;
  };
}

Remember, practice is key to mastering a concept. So, keep practicing and happy coding!