Custom Validators for Angular Reactive Forms
Tags: angular, validation, forms, factory
Published: 2020-05-24 00:22:36

---
title: Custom Validators for Angular Reactive Forms
published: true
description: Angular's Reactive Forms allow a pretty cool factory-style pattern for building custom validators.
tags: angular, validation, forms, factory
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/nhoi6k9aej227ck9zysy.jpg
---

Angular… ugh, I know.

But Reactive Forms are actually pretty cool and once you get the hang of them, you can appreciate all of the form dynamics that Angular handles for you.

At the end of the day we want to supply an object to a form, allow the user to make changes and keep those changes valid.

Angular provides classes to marry object properties to markup and a convenient [`FormBuilder`](https://angular.io/api/forms/FormBuilder) to help construct groups of form components with default values.

```javascript
this.formGroup = formBuilder.group([
  {
    "name": [
      "Adam",
    ],
    "age": [
      "21"
    ]
  }
]);
```

Out of the box Angular [provides](https://angular.io/api/forms/Validators) a set of validators that cover many if not most requirements.

My name should be longer than 20 characters and clearly I’m no older than 25 so I can specify validators to the FormBuilder object:

```javascript
this.formGroup = formBuilder.group([
  {
    "name": [
      "Adam", Validators.maxLength(20)
    ],
    "age": [
      "21", Validators.max(25)
    ]
  }
]);
```

We can check for validation errors in our `FormGroup` object with the errors property of each control. This property maintains an object that, when the value is valid, is empty. Otherwise, the object contains keys indicating how the value has failed validation.

For example, if our name value was say 28 characters, longer than the valid 20 characters,

```javascript
formGroup.get("name").errors
```

would return:

```json
{
	maxLength: {
		actualLength: 28,
		requiredLength: 20
	}
}
```

If we need something a little extra, something outside the typical `min`, `max`, `required` or `email`, we can write a custom validator.

Suppose we wanted to validate a favorite movie field. Let’s add a movie validator and determined that any value other than Back To The Future is invalid:

```javascript
function movie(control: AbstractControl)
	: { [key: string]: any } {
    if (control.value !== "Back To The Future") {
        return {
          movie: {
            suppliedMovie: control.value,
            quality: "Questionable"
          }
        };
    }

    return undefined;
}
```

Here we check the control’s value and it’s not the value we want we can return an object specify how the value is invalid. If the value is valid, we return undefined because we don’t want the errors object to be populated in this case.

It’s a simple change to add our new validator to the FormBuilder call:

```javascript
this.formGroup = formBuilder.group({
  name: ["Adam", Validators.maxLength(25)],
  age: ["21", [Validators.min(0), Validators.max(25)]],

  // Instead of using the Validators class we can 
  // supply our own validator, movie:
  favoriteMovie: ["Silent Running", movie]
});
```

What if we wanted to be less stringent and maybe offer the user of our validation function the option to specify a number of movies that could be favorites.

Now we need an argument to `movie`, like `max` and `maxLength` do. 

```javascript
function movie(
  validMovies: string[]
): ValidatorFn {
  return (control: AbstractControl): { [key: string]: any } => {
    if (validMovies.indexOf(control.value) == -1) {
      return {
        movie: {
          suppliedMovie: control.value,
          reason: "Not one of my favorites!"
        }
      };
    }
  };
}
```

Instead of the `movie` function now immediately validating the value and returning an error object, it’s basically a higher-order function and returning a function that Reactive Forms will use. We provide an array of movie names as an argument, and these are used by the function at validation time to check the control’s value.

```javascript
this.formGroup = formBuilder.group({
  name: ["Adam", Validators.maxLength(25)],
  age: ["21", [Validators.min(0), Validators.max(25)]],

  favoriteMovie: ["Silent Running",
	movie([
		"Teen Wolf", 
		"Saving Private Ryan", 
		"Inception"
	])]
});
```

Now, Silent Running (excellent movie, [criminally downvoted](https://www.imdb.com/title/tt0067756/)) is still invalid, but we’ve supplied a list of movies for which the value will be valid.

Check out the StackBlitz for this example!
{% stackblitz angular-ivy-vpvfxr %}