Thought Leadership
Aug 17, 2022

Powering up your forms with Custom Validators

How to use Angular’s 11 validators to your advantage

Powering up your forms with Custom Validators

Since the Internet began, forms have been the most popular way to obtain information from users. Nowadays, there are other smooth ways to extract user data like integrations with social networks, email platforms, and even video platforms, but forms always are the first option when we need to know something about our users.

Whenever you have forms, you’re going to need validators.

In this post, we are going to get into how you can build custom validators in Angular to power up your forms.

Built-in Validators

Angular provides 11 built-in validators you can use. Those validators were made to cover common use cases. In that list we find:

  • min and max, to set a minimum and maximum control value.
  • required, to set a control should have a value and requiredTrue, to specify a control normally a checkbox, should be true.
  • email, to indicate that control value should have an email pattern.

But, what about if we need something more complex and specific to problem we are trying to solve? Here is when custom validators come in handy.

Understanding Reactive Forms Basics

To validate a specific input using reactive forms you need to add the validator function directly to the form control model in the component class. Then angular will call these functions whenever the value of control changes.

Validator functions

As Angular docs form validations mention, validator functions can be:

Synchronous and Asynchronous

* For performance reasons, Angular only runs async validators if all sync validators pass. Each must complete before errors are set.

Sync Validators Structure

The following example show the structure of a custom sync validation function.

export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
  return (control: AbstractControl): ValidationErrors | null => {
    const forbidden = nameRe.test(control.value);
    return forbidden ? {forbiddenName: {value: control.value}} : null;
  };
}
// Example extracted from https://angular.io/guide/form-validation#defining-custom-validators

The custom validator above will detect if a given value can’t match with a given regular expression. There are several interesting points to analyze in our validator function.

The validator function must return a ValidatorFn (line 1), which is an interface of a function with following parameter: control of type AbstractControl.

AbstractControl is the base class for FormControls, FormGroups and FormArrays that means we are able to get access to all properties of that control and that’s exactly what we can see in line 3.

const forbidden = nameRe.test(control.value);

Something important to know about a ValidatorFn is:

A function that receives a control and synchronously returns a map of validation errors if present, otherwise null.

{forbiddenName: {value: control.value}} : null

Line 4 shows the expected value for true or false values in our custom validation. If name is forbidden, this will return a map of validation errors, otherwise null.

For this particular scenario, null means that provided name is not forbidden, which is OK, If a map is returned, that means the value is forbidden.

Async Validators

This type of validator was designed to avoid expensive as validation processes, it implements the AsyncValidatorFunction and AsyncValidator interfaces.

The next code example shows how to create an async custom validator.

@Injectable({ providedIn: 'root' })
export class UniqueAlterEgoValidator implements AsyncValidator {
  constructor(private heroesService: HeroesService) {}

  validate(
    control: AbstractControl
  ): Observable<ValidationErrors | null> {
    return this.heroesService.isAlterEgoTaken(control.value).pipe(
      map(isTaken => (isTaken ? { uniqueAlterEgo: true } : null)),
      catchError(() => of(null))
    );
  }

In line 2 we can see UniqueAlterEgoValidator implements the AsyncValidator interface. The validate function in line 5, which comes from implementation of AsyncValidator must return 2 types.

Something interesting to know about async validators:

Asynchronous validation happens after the synchronous validation, and is performed only if the synchronous validation is successful.

map(isTaken => (isTaken ? { uniqueAlterEgo: true } : null)),

Line code extracted from UniqueAlterEgoValidator.ts line 9 shows if the name was taken, validation will return a map of error with key uniqueAlterEgo and value true, if not, just return null.

Now you have a deep understanding of how Sync and Async validators work, what is behind the implementation of these types of validators, and more important, how to build your own custom validators.

. . .
Article Summary
Learn how to build custom validators in Angular, including both synchronous and asynchronous types, to enhance your forms and create complex validation logic tailored to your needs.
Author
Andres Villanueva
Senior Software Engineer
Related Articles
Open Source Insights Delivered Monthly

By clicking “submit” I acknowledge receipt of our Privacy Policy.

Thanks for signing up for our Newsletter! We look forward to connecting with you.
Oops! Something went wrong while submitting the form.