Carl Rippon

Building SPAs

Carl Rippon
BlogBooks / CoursesAbout
This site uses cookies. Click here to find out more

Angular Async Validation

January 11, 2016
angulartypescript

I recently posted about Angular 2 Form validation which didn’t cover asynchronous validation. This post does cover an async validation example …

I’ve put all the classes in the same file to make the example simpler to show and read. The example gives a textbox for the user to enter a product code and validates that the product code exists and puts a description at the side if it does.

Referencing an async validator in FormBuilder

The first key point is that you reference the async validator in the 3rd parameter in FormBuilder.group(). None async validators go in the 2nd parameter - I only have a async validator in my example.

constructor(builder: FormBuilder) {
    this.orderForm = builder.group({
        productCode: ["", , ProductValidator.productExists]
    });
}

Writing the async validator

The async validator needs to return a promise that in turn returns null if valid or something else if not valid. In this example, I am calling a ProductService class to check whether the product exists

class ProductValidator {
  static productExists(control: Control): { [key: string]: any } {
    return new Promise(resolve => {
      var productService = new ProductService();
      var product: Product;
      productService.getProduct(control.value).then(productFound => {
        if (productFound == null) {
          // need to return something if not ok
          resolve({ productExists: false });
        } else {
          // need to return null if ok
          resolve(null);
        }
      });
    });
  }
}

Here’s the full listing …

import { Component } from "angular2/core";
import {
  FORM_DIRECTIVES,
  Control,
  ControlGroup,
  FormBuilder,
  Validators
} from "angular2/common";

class Product {
  code: string;
  description: string;
}

class ProductService {
  public getProduct(productCode: string): Promise<Product> {
    return new Promise(resolve => {
      // simulate a call to a web api with a setTimeout()
      setTimeout(() => {
        // pretent these are our products in our database
        var products: Product[] = [
          { code: "P001", description: "Chai" },
          { code: "P002", description: "Tofu" },
          { code: "P003", description: "Pavlova" }
        ];
        // this is the product code we want to check exists
        var productCodeToCheck: string = productCode;
        // check if the product code exists
        var foundProduct: Product;
        products.forEach(function(product: Product) {
          if (productCodeToCheck === product.code) {
            foundProduct = product;
          }
        });
        resolve(foundProduct);
      }, 1000);
    });
  }
}
class ProductValidator {
  static productExists(control: Control): { [key: string]: any } {
    return new Promise(resolve => {
      var productService = new ProductService();
      var product: Product;
      productService.getProduct(control.value).then(productFound => {
        if (productFound == null) {
          // need to return something if not ok
          resolve({ productExists: false });
        } else {
          // need to return null if ok
          resolve(null);
        }
      });
    });
  }
}

@Component({
  selector: "my-app",
  template: `
    <form [ngFormModel]="orderForm">
      <div>
        <input
          #productCode
          type="text"
          placeholder="product code"
          ngControl="productCode"
          (keyup)="lookupDescription(productCode.value)"
        />
        <span>{{ productDescription }}</span>
      </div>
    </form>
  `,
  styles: [
    `
      .ng-invalid {
        border-left: 5px solid #a94442;
      }
    `
  ],
  directives: [FORM_DIRECTIVES]
})
export class AppComponent {
  public productDescription: string;
  orderForm: ControlGroup;
  constructor(builder: FormBuilder) {
    this.orderForm = builder.group({
      productCode: ["", , ProductValidator.productExists]
    });
  }
  lookupDescription(productCode) {
    // initialise the description before we lookup the description
    this.productDescription = "";
    // call a service to lookup the description
    var productService = new ProductService();
    var product: Product;
    productService.getProduct(productCode).then(productFound => {
      if (productFound) {
        // set the components productDescription which will automatically show in the UI
        this.productDescription = productFound.description;
      }
    });
  }
}

If you to learn more about TypeScript, you may find my free TypeScript course useful:

Learn TypeScript

Learn TypeScript
Take a look

Want more content like this?

Subscribe to receive notifications on new blog posts and courses

Required
© Carl Rippon
Privacy Policy