Injecting Providers into Async Custom Validators in Angular

One of the first things developers do when we are building a new app is a signup form, that checks that every field is okay and shows an appropriate error message if it’s not.

It’s also a common practice to check if the typed email is already registered or if the username is taken. Angular has some basic validators built in, but in order to check our API and return the status we need to write a custom async validator. Please read this great guide about Advanced Forms & Validation in Ionic 2 by Josh Morony first.

The only part missing in the guide is to actually inject the provider in charge of connecting to our API and getting the result. This is a bit tricky since async validators need to have static properties and we can’t inject a provider directly, so we will have to pass it to the validator class by using its constructor. This will work from Angular 2 onwards, I’m using Ionic 2.

Here is how I solved it. If you know any way to improve this code, please leave a comment with a link to your code!

import { FormControl } from '@angular/forms';
import { UserService } from '../../providers/user-service/user-service';

export class UserValidator {
  static userService: UserService;
  static EMAIL_REGEXP = /.+@.+\..+/;

  constructor(userService: UserService) {
    UserValidator.userService = userService;
  }

  // Async validator: `new UserValidator(userService).checkEmailAvailable`

  checkEmailAvailable(input: FormControl) {
    if (!input.value || !UserValidator.EMAIL_REGEXP.test(input.value)) {
      return Promise.resolve({ invalid: true });
    }

    return UserValidator.userService.checkEmail(input.value)
    .then(data => data && data.available ? null : data)
    .catch(err => console.error(err));
  }
}

We start making our class properties static, then we inject the provider UserService into the constructor. Because we have defined userService as static, we can assign the reference to the provider by using the Class.property syntax.

The validator function is actually very simple: it returns null if there aren’t any errors, or an object with every error set to true. My API actually returns { available: true } if everything is okay and then I convert that to null, but you may handle this differently.

Now every time we want to use this validator, we need to call it from our parent class like new UserValidator(userService).checkEmailAvailable .

With this snippet, it would be fairly simple to create another validator function inside the UserValidator class to check if a username is taken or not.