Posted by Arjun on Friday 17th February 2017

Angular 2 - Async Validator - Username/email availability check

In this post I will show you, how we can create custom async validator to check for email availability, same logic you can apply for username or other async validations.

Lets create a component:

import { Component, OnInit } from '@angular/core';
import { Validators, FormBuilder, FormGroup, FormControl } from '@angular/forms';
import { UserService } from '../services/user.service';

@Component({
  selector: 'signup',
  template: `<form novalidate [formGroup]="form" (ngSubmit)="onSubmit()" name="signupForm">
    <div class="form-group">
    <label for="login">Email Address<span class="requiredSymbol">*</span></label>
    <input autofocus pInputText type="email" id="username" formControlName="email" name="email" class="form-control form-control-lg"
        placeholder="Enter Your Email Address">
        <span *ngIf="form.get('email').dirty || formSubmitclicked">
        <small class="error" *ngIf="form.get('email').hasError('isEmailUnique')">This email has been registered already</small>
        <small class="error" *ngIf="form.get('email').hasError('required')">The Email field is required.</small>
        <small class="error" *ngIf="form.get('email').hasError('pattern')">The Email field must contain a valid email address.</small>
    </span>
    </div>
    <div class="form-group text-center">
    <button type="submit" pButton class="btn btn-success btn-lg btn-block" label="Singup"></button>
    </div>
</form>`,
})
export class SignupComponent {

  form: FormGroup;

  constructor(private userService: UserService, private fb: FormBuilder) {
      this.buildForm();
  }

  buildForm(): void {
    this.form = this.fb.group({
      "email": ["", [
             Validators.required,
             Validators.minLength(3)
           ],
           this.isEmailUnique.bind(this) // async Validator passed as 3rd parameter 
      ]
    });
  }

  isEmailUnique(control: FormControl) {
    const q = new Promise((resolve, reject) => {
      setTimeout(() => {
        this.userService.isEmailRegisterd(control.value).subscribe(() => {
          resolve(null);
        }, () => { resolve({ 'isEmailUnique': true }); });
      }, 1000);
    });
    return q;
  }


  onSubmit() {
  
  }

}

Here is my user service:

import { Injectable } from '@angular/core';
import { Http, Response, Headers } from '@angular/http';
import { Observable } from 'rxjs/Observable';


@Injectable()
export class UserService {

    constructor(private http: Http) { }

    isEmailRegisterd(email: string) {
        var headers = new Headers();
        headers.append('Content-Type', 'application/json');
        return this.http.post('http://localhost:8080/api/v1/isEmailRegisterd', JSON.stringify({ email: email }), { headers: headers })
            .map((response: Response) => response.json())
            .catch(this.handleError);
    }

    private handleError(error: any) {
        console.log(error);
        return Observable.throw(error.json());
        ;
    }


}

Backed server response for unregistered email will be null and for registered email address will be as shown below -

{
"statusCode": 400,
"error": "Bad Request",
"message": "Email address already registerd"
}