DEV Community

Carlos Dubón
Carlos Dubón

Posted on

📋 Create a custom form validator

Have you ever wanted to implement a custom form validator in Angular? 🅰️ It is possible by creating your own validator function.

For our example let’s say we have a social media app 📱 and we want to only ☝️ register users over 16 years old 🧑.

We can simply use the built in 🧰 Validators.min(n) validator for this case, but we are going to create our own custom validator function from scratch ✨

*click* Noice - GIF

I’m going to be using reactive forms ⚛️ for this example so make sure to import the ReactiveFormsModule in your app.module.ts

// app.module.ts

import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms'; // 👈
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CustomFormValidatorComponent } from './custom-form-validator/custom-form-validator.component';

@NgModule({
  declarations: [AppComponent, CustomFormValidatorComponent],
  imports: [BrowserModule,
            AppRoutingModule,
            ReactiveFormsModule // 👈
           ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

I’ve created a component named custom-form-validator and I’ve already produced some boilerplate code for us to work with

magic GIF

<!-- custom-form-validator.component.html -->

<form [formGroup]="user">
  <input formControlName="name" type="text" />
  <input formControlName="age" type="number" min="0" />
  <button (click)="signUp()" [disabled]="!user.valid">Sign up</button>
</form>
Enter fullscreen mode Exit fullscreen mode

Our form is pretty simple, an input for the name and another one for the age. The submit button is disabled if the form is not valid.

// custom-form-validator.component.ts

import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-custom-form-validator',
  templateUrl: './custom-form-validator.component.html',
  styleUrls: ['./custom-form-validator.component.scss'],
})
export class CustomFormValidatorComponent implements OnInit {
  public user: FormGroup;

  constructor(private _fb: FormBuilder) {
    this.user = this._fb.group({
      name: ['', [Validators.required]],
      age: [12, [Validators.required]],
    });
  }

  public ngOnInit(): void {}

  public signUp(): void {
    console.log("Success ✅");
  }
}
Enter fullscreen mode Exit fullscreen mode

Right now, we only have 1 validator (required) set to each input. This makes it so that a user with any age 🚨 can sign up to our app.

oops GIF

Let’s create our own validator function.

Again, I repeat myself, one could simply use the built in Validators.min(n) to solve this problem 🤷. But for educational purposes we are going to create our own validator. I’m sure you’ll encounter more complex situations 🧪🧬 in which a custom validator would come in pretty handy. That being said let's proceed.

crazy GIF

// age.validator.ts

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

export function isOverAge(age: number): ValidatorFn {
  return (control: AbstractControl): { [key: string]: any } | null => {
    const formControlValue: number = control.value;

    // 👇 perform your validations below
    if (formControlValue >= age) {
      return null;
    } else {
      return { invalidAge: formControlValue };
    }
  };
}
Enter fullscreen mode Exit fullscreen mode

I’ve created the isOverAge(n) validator function and exported it 📦 (very important step). A validator function returns null if it is valid else it should return an object.

In this case I’ve returned an object containing the invalid age.

Now let’s consume this validator function in our reactive form in custom-form-validator.component.ts

// custom-form-validator.component.ts

import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { Component, OnInit } from '@angular/core';
import { isOverAge } from '../validators/age.validator'; // 👈 Notice the import

@Component({
  selector: 'app-custom-form-validator',
  templateUrl: './custom-form-validator.component.html',
  styleUrls: ['./custom-form-validator.component.scss'],
})
export class CustomFormValidatorComponent implements OnInit {
  public user: FormGroup;

  constructor(private _fb: FormBuilder) {
    this.user = this._fb.group({
      name: ['', [Validators.required]],
      age: [12, [Validators.required, isOverAge(16)]], // 👈 Consuming the validator function
    });
  }

  public ngOnInit(): void {}

  public signUp(): void {
    console.log("Success ✅");
  }
}
Enter fullscreen mode Exit fullscreen mode

And we are done ✨! Now only users over 16 years old are going to be able to submit our form.

WOW GIF

If we subscribe to valueChanges of our form and log the age errors we’ll see how our validator function is working as expected:

import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { Component, OnInit } from '@angular/core';
import { isOverAge } from '../validators/age.validator';

@Component({
  selector: 'app-custom-form-validator',
  templateUrl: './custom-form-validator.component.html',
  styleUrls: ['./custom-form-validator.component.scss'],
})
export class CustomFormValidatorComponent implements OnInit {
  public user: FormGroup;

  constructor(private _fb: FormBuilder) {
    this.user = this._fb.group({
      name: ['', [Validators.required]],
      age: [12, [Validators.required, isOverAge(16)]],
    });
  }

  public ngOnInit(): void {
    this.user.valueChanges.subscribe(() => {
      console.log(this.user.get('age')?.errors); // 👉 null if the age is valid else {invalidAge: n}
    });
  }

  public signUp(): void {
    console.log("Success ✅");
  }
}
Enter fullscreen mode Exit fullscreen mode

wrap GIF

To wrap things up 🎁 I want to point out that 🅰️ Angular has many useful built-in validators that cover most of the spectrum. To see a complete list of these visit @angular/forms Validators By all means use these instead of a custom form validator if possible.

This is my first post and I hope to continue bringing awesome content to the platform. 🦄

Discussion (0)