Implementing Angular 2 Form Validation

Implementing AngularJS Form Validation Step by Step

In lab 6 we have covered basic fundamentals of Angular like module, components, directives, expression and so on. To see this lab read lab 6 from here.

In lab 7 we will look in to how to create Single page applications using Angular routing. To see Lab 7 you need to read lab 7 from here.

AngularJS Form Validation Step by Step : In this lab we will try to understand how we can implement Angular 2 Form Validation using Angular framework. In case you are new to Angular please our Learn Angular video series

Requirement of a good validation structure

One of the important legs of software application is validations.

Validations are normally applied to user interfaces (Forms) and User interfaces have controls and on the controls, we apply validations.

 

In Angular form validation architecture structure is as follows.

At the top we have FormGroup , FormGroup has FormControls and FormControls has one or many validations.

There are 3 broader steps to implement Angular validation

  1. Create FormGroup.
  2. Create FormControl and with proper validations.
  3. Map those validations to the HTML Form.

What kind of validation will we implement in this Lab ?

In this lab we will implement the following validation on our Customer screen: -

  • Customer Name should be compulsory.
  • Customer code should be compulsory.
  • Customer code should be in the format of A1001, B4004 and so on. In other words the first character should be a capital alphabet followed by 4 letter numeric.

Note :- Customer code has composite validations.

Where to put Validations ?

Before even we start with validations we need to decide which is the right place to put validations. If you see in Angular we have three broader section UI , Component and Model. So let's think over which is the right layer to put validations.

  • UI :- UI is all about look and feel and positioning of controls. So putting validation in this layer is a bad idea. Yes on this layer we will apply validations but the validation code should be in some other layer.
  • Component :- Component is the code behind ( Binding code) which binds the UI and Model. In this layer more binding related activity should be put. One component can use many models so if we put the validation logic here we need to duplicate that in other components as well.
  • Model :- A Model represents a real world entity like person , user , admin , product etc. Behavior a.k.a validations are part of the model. A model has Data and Behavior. So the right place where validations should be present is this Layer.

So let us put validation as a part of the model.

Step 1 :- Import necessary components for Angular validators

So the first step is to import the necessary components for angular validators in the customer model. All angular validator components are present in "@angular/forms" folder. We need to import five components NgForm, FormGroup, FormControl and validators.

NgForm :- Angular tags for validation.
FormGroup :- Helps us to create collection of validation.
FormControl and Validators:- Helps us to create individual validation inside FormGroup.
FormBuilder :- Helps us to create the structure of Form group and Form controls. Please note one Form group can have many FormControls.

import {NgForm,
    FormGroup,
    FormControl,
    Validators,
    FormBuilder } from '@angular/forms'

Step 2 :- Create FormGroup using FormBuilder

The first step is to create an object of FormGroup in which we will have collection of validation. The FormGroup object will be constructed using the "FormBuilder" class.

formGroup: FormGroup = null; // Create object of FormGroup
var _builder = new FormBuilder();
this.formGroup = _builder.group({}); // Use the builder to create object

Step 3 :- Adding a simple validation

Once the FormGroup object is created the next step is to add controls in the FormGroup collection. To add a control we need to use "addControl" method. The first parameter in "addControl" method is the name of the validation and second is the Angular validator type to be added.

Below is a simple code in which we are adding a "CustomerNameControl" using the "Validators.required" FormControl. Please note "CustomerNameControl" is not a reserved keyword. It can be any name like "CustControl".

this.formGroup.addControl('CustomerNameControl', new
            FormControl('',Validators.required));

Step 4 :- Adding a composite validation

If you want to create a composite validation then you need to create a collection and add it using "compose" method as shown in the below code.

var validationcollection = [];
validationcollection.push(Validators.required);
validationcollection.push(Validators.pattern("^[A-Z]{1,1}[0-9]{4,4}$"));
this.formGroup.addControl('CustomerCodeControl', new FormControl('', Validators.compose(validationcollection)));

Full Model code with validation

Below is the full Customer Model code with all three validation as discussed in the previous section. We have also commented the code so that you can follow it.

// import components from angular/form
import {NgForm,
    FormGroup,
    FormControl,
    Validators,
    FormBuilder } from '@angular/forms'
export class Customer {
    CustomerName: string = "";
    CustomerCode: string = "";
    CustomerAmount: number = 0;
    // create object of form group
    formGroup: FormGroup = null;
    
    constructor(){
        // use the builder to create the 
        // the form object
        var _builder = new FormBuilder();
        this.formGroup = _builder.group({});

        // Adding a simple validation
        this.formGroup.addControl('CustomerNameControl', new
            FormControl('',Validators.required));
        
        // Adding a composite validation
        var validationcollection = [];
        validationcollection.push(Validators.required);
        validationcollection.push(Validators.pattern("^[A-Z]{1,1}[0-9]{4,4}$"));
        this.formGroup.addControl('CustomerCodeControl', new
            FormControl('', Validators.compose(validationcollection)));
    }   
}

Step 5 :- Apply formGroup to HTML form

The next thing is to apply ‘formGroup' object to the HTML form. For that we need to use "[formGroup]" angular tag in that we need to specify the "formGroup" object exposed via the customer object.

<form [formGroup]="CurrentCustomer.formGroup">
</form>

Step 6:- Apply validations to HTML control

The next step is to apply formgroup validation to the HTML input controls. That's done by using "formControlName" angular attribute. In "formControlName" we need to provide the form control name which we have created while creating the validation.

<input type="text" formControlName="CustomerNameControl"
[(ngModel)]="CurrentCustomer.CustomerName">

Step 7:- Check if Validations are ok

When user starts filling data and fulfilling the validations we would like to check if all validations are fine and accordingly show error message or enable / disable UI controls.

In order to check if all validations are fine we need to use the "valid" property of "formGroup". Below is a simple example where the button will be disabled depending on whether validations are valid or not. "[disabled]" is an angular attribute which enables and disables HTML controls.

<input type="button" 
value="Click" [disabled]="!(CurrentCustomer.formGroup.valid)"/>

Step 8:- Checking individual validations

"CurrentCustomer.formGroup.valid" check all the validations of the "FormGroup" but what if we want to check individual validation of a control.

For that we need to use "hasError" function. "CurrentCustomer.formGroup.controls['CustomerNameControl'].hasError('required')" checks that for "CustomerNameControl" has the "required" validation rule been fulfilled. Below is a simple code where we are displaying error message in a "div" tag which visible and not visible depending on whether the "hasError" function returns true or false.

Also note the "!" ( NOT) before "hasError" which says that if "hasError" is true then hidden should be false and vice versa.

<div  [hidden]="!(CurrentCustomer.formGroup.controls['CustomerNameControl'].hasError('required'))">Customer name is required </div>

Step 9 :- standalone elements

In our forms we have three textboxes "CustomerName" , "CustomerCode" and "CustomerAmount". In these three textboxes only "CustomerName" and "CustomerCode" has validations while "CustomerAmount" does not have validations.
Now this is bit funny but if we do not specify validations for a usercontrol which is inside a "form" tag which has "formGroup" specified you would end up with a long exception as shown below.

Error: Uncaught (in promise): Error: Error in ../UI/Customer.html:15:0 caused by:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ngModel cannot be used to register form controls with a parent formGroup directive.&nbsp; Try using
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; formGroup's partner directive "formControlName" instead.&nbsp; Example:

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;

      
    <div [formGroup]="myGroup">
      <input formControlName="firstName">
    </div>

    In your class:

    this.myGroup = new FormGroup({
       firstName: new FormControl()
    });

      Or, if you'd like to avoid registering this form control, indicate that it's standalone in ngModelOptions:

      Example:

      
    <div [formGroup]="myGroup">
       <input formControlName="firstName">
       <input [(ngModel)]="showMoreControls" [ngModelOptions]="{standalone: true}">
    </div>
  
Error: 
      ngModel cannot be used to register form controls with a parent formGroup directive.  Try using
      formGroup's partner directive "formControlName" instead.  Example:

      
    <div [formGroup]="myGroup">
      <input formControlName="firstName">
    </div>

    In your class:

    this.myGroup = new FormGroup({
       firstName: new FormControl()
    });

      Or, if you'd like to avoid registering this form control, indicate that it's standalone in ngModelOptions:

      Example:

      
    <div [formGroup]="myGroup">
       <input formControlName="firstName">
       <input [(ngModel)]="showMoreControls" [ngModelOptions]="{standalone: true}">
    </div>
    
    

The above error can be simplified in three simple points : -

  1. It says that you have enclosed a HTML control inside a HTML FORM tag which has Angular form validations.
  2. All controls specified inside HTML FORM tag which have angular validation applied SHOULD HAVE VALIDATIONS.
  3. If a HTML control inside Angular form validation does not have validation you can do one of the below things to remove the exception: -
  • You need to specify that it's a standalone control.
  • o Move the control outside the HTML FORM tag.

Below is the code how to specify "standalone" for Angular validations.

<input type="text" [ngModelOptions]="{standalone:true}"
[(ngModel)]="CurrentCustomer.CustomerAmount">

Complete code of Customer UI with validations applied

Below is the complete Customer UI with all three validations applied to "CustomerName" and "CustomerCode" controls.

<form [formGroup]="CurrentCustomer.formGroup">
<div>
Name:
<input type="text" formControlName="CustomerNameControl"
[(ngModel)]="CurrentCustomer.CustomerName"><br /><br />
<div  [hidden]="!(CurrentCustomer.formGroup.controls['CustomerNameControl'].hasError('required'))">Customer name is required </div>
Code:
<input type="text" formControlName="CustomerCodeControl"
[(ngModel)]="CurrentCustomer.CustomerCode"><br /><br />
<div  [hidden]="!(CurrentCustomer.formGroup.controls['CustomerCodeControl'].hasError('required'))">Customer code is required </div>
<div  [hidden]="!(CurrentCustomer.formGroup.controls['CustomerCodeControl'].hasError('pattern'))">Pattern not proper </div>

Amount:
<input type="text" 
[(ngModel)]="CurrentCustomer.CustomerAmount"><br /><br />
</div>
{{CurrentCustomer.CustomerName}}<br /><br />
{{CurrentCustomer.CustomerCode}}<br /><br />
{{CurrentCustomer.CustomerAmount}}<br /><br />
<input type="button" 
value="Click" [disabled]="!(CurrentCustomer.formGroup.valid)"/>
</form>

Run and see your validation in action

Once you are done you should be able to see the validation in action as shown in the below figure.

Dirty, pristine, touched and untouched

In this lab we covered "valid" and "hasError" property and function. "formGroup" also has lot of other properties which you will need when you are working with validations. Below are some important ones.

Property Explanation
dirtyThis property signals if Value has been modified.
pristine This property says if the field has changed or not.
touched When the lost focus for that control occurs.
untouched The field is not touched.
+91-22-66752917
+91 9967590707
questpond@questpond.com / questpond@yahoo.com