Simplify Angular - Custom Validators

Here is a quick guide for custom form validation. Angular’s data binding provides plenty of ways to validate the form data you are about to pass back to your server, but Angular’s reactive forms provide a nice way to add your validation.

Build Your Form

Create your input in your components template

1<!-- component HTML -->
2<form [formGroup]="myForm">
3  ...
4  <input formControlName="myInput" />
5  ...
6</form>
 1//Component Typescript
 2import { FormGroup, FormBuilder, FormControl } from '@angular/forms';
 3...
 4myForm: FormGroup;
 5...
 6constructor(private readonly formBuilder: FormBuilder) { }
 7...
 8this.myForm = this.formBuilder.group({
 9    myInput: new FormControl('', [])
10})

Create Your Validators

Angular validators take an AbstractControl as a parameter and return the error name key and a boolean value of true if it is invalid, or null if the input is valid.

 1//Component Typescript
 2import { FormGroup, FormBuilder, FormControl, Validators } from '@angular/forms';
 3...
 4this.myForm = this.fromBuilder.group({
 5    myInput: new FormControl('', [this.requiredTrimmed])
 6})
 7...
 8requiredTrimmed(control: AbstractControl): {[key: string]: any | null} {
 9  const key = 'required-trimmed';
10  const error = { [key]: true };
11  const value = control.value;
12
13  if (value === undefined) { return error; }
14  if (value === null) { return error; }
15  if (value.trim().length <= 0) { return error; }
16
17  return null;
18}

Easy enough when your validators are simple like this one, but what happens when you need to pass in some parameters.

 1//Component Typescript
 2import { ValidatorFn, AbstractControl } from '@angular/forms';
 3...
 4minLengthTrimmed(min: number): ValidatorFn => {
 5  return (control: AbstractControl): { [key: string]: boolean } | null => {
 6    const key = 'min-length-trimmed';
 7    const error = { [key]: true };
 8    const value = control.value;
 9
10    if (value === undefined) return error;
11    if (value === null) return error;
12    if (value.trim().length < min) return error;
13
14    return null;
15  }
16}

Clean It Up

Typically You are going to use these validators in more than one component throughout your app, so lets create ourselves a custom validator helper class.

 1import { ValidatorFn, AbstractControl } from '@angular/forms';
 2
 3export class AppValidators {
 4  static minLengthTrimmed(min: number): ValidatorFn => {
 5    return (control: AbstractControl): { [key: string]: boolean } | null => {
 6      const key = 'min-length-trimmed';
 7      const error = { [key]: true };
 8      const value = control.value;
 9
10      if (value === undefined) return error;
11      if (value === null) return error;
12      if (value.trim().length < min) return error;
13
14      return null;
15    }
16  }
17}

Now our form building looks like this:

1//Component Typescript
2...
3this.myForm = this.fromBuilder.group({
4    myInput: new FormControl('', [AppValidators.minLengthTrimmed(3)])
5})
6...

Hope this helps! Stay tuned for a post on AsyncValidators. I’ve started an open source library of custom validators, so if you have any suggestions for new validators please let me know here or on Github!

NPM: https://www.npmjs.com/package/more-validators
Github: https://github.com/ChekTek/more-validators