Form Validators

Validators are rules which an input control has to follow. If the input doesn’t match the rule then the control is said to be invalid.

In our form, Some fields are required, for the title we will use minimum length 10, and for the color we will use the pattern for the alphabets only. For price we will use the minimum value validation.

We can apply the validators by adding attributes to the template, or by adding validators in form model.

Now we are working with the model-driven forms, so we will see the Validators in the model-driven form.

In the next chapter, we will see the Validation by adding attributes to the template.

Angular comes with the small set of prebuilt validators to match the once we can define via standard HTML 5 attributes, namely required, minlength, maxlength and pattern which we can access from the Validators class in @angular/forms library.

To use this Validators, we need to import the Validators class from @angular/forms library in add-product-model.component.ts, as shown below

import { Validators } from '@angular/forms';

We need to insert the validators while we are initializing the Form Control Object.

Syntax to add Validators in FormControl is shown below.

FormControl(<initial value>,<list of Validators>)

For Example, 

this.title = new FormControl(‘’,Validators.required);

This will initialize title form control with the initial value as an empty string, and make it as required field.

We can add the multiple validations like required and minlength by using Validators in an array.

As shown below,

this.title = new FormControl('', [Validators.required, Validators.minLength(10)]);

Same way, we will add validators for the other form controls. as shown below

this.title = new FormControl('', [Validators.required, Validators.minLength(10)]);
this.modelName = new FormControl();
this.color = new FormControl('', Validators.pattern('[a-zA-Z]*'));
this.productType = new FormControl('', Validators.required);
this.brand = new FormControl('', Validators.required);
this.price = new FormControl('', [Validators.required, Validators.min(1)]);

We have inserted the validations in the form control. Now we need to show error with visual feedback if the control is invalid.

Form Control has different states based on which we can show the error message and hide it when the control is valid.

The form control instance on our model encapsulates the state of the control itself, such as if it is currently valid or invalid.

We will understand the form control states using following example.

add following code for title form control on add-product-model.component.html

<div class="form-group">
            <label>Title</label>
            <input type="text" class="form-control" formControlName="title">
            <div class="alert alert-info" role="alert">
              <strong>Valid ? </strong>{{title.valid}} <br>
              <strong>Invalid ? </strong>{{title.invalid}}<br>
              <strong>Touched ? </strong>{{title.touched}}<br>
              <strong>Untouched ? </strong>{{title.untouched}}<br>
              <strong>Dirty ? </strong>{{title.dirty}}<br>
              <strong>Pristine ? </strong>{{title.pristine}}<br>
            </div>
</div>

This code will give output as below,

Form Control State
Form Control State

Now lets understand each form control state.

Dirty property is true if a user changes the value of form control.

Pristine is the opposite of dirty.

Pristine property is true if a user doesn’t change the value of form control, otherwise, it is false.

As you can see in the above example initially pristine was true, and dirty was false, but when you change the input value dirty becomes true, and pristine becomes false.

A form control is said to be touched if a user focused on the control and then focused on something else.

For example by clicking into the control and then pressing tab or clicking on another control in the form.

The difference between touched and dirty is that with touched the user doesn’t need to actually change the value of the input control.

As shown in the above example touched is false initially, and untouched is true, once we focus on input box and click outside the control, touched becomes true and untouched becomes false.

Valid is true if the field doesn’t have validators or all its validators are passing.

Again the opposite of valid is invalid.

title property has two validators, required and minlength, so as shown above initially valid is false, and invalid is true, once all validators are passed, valid becomes true and invalid becomes false.

Bootstrap provides a different way to show an error message to the user.

  • By using Alert component of bootstrap
  • By using visual feedback for the form controls when they are invalid.

Lets first see the validation style using Alert Component.

<div class="form-group">
            <label>Title</label>
            <input type="text" class="form-control" formControlName="title">
            <div class="alert alert-danger" role="alert" *ngIf="title.errors && (title.touched || title.dirty)">              
              Invalid Title
            </div>
</div>

This will show the alert message for invalid title input as below.

Validation Styling Alert Component

In this type of style, we will use the alert component of bootstrap as shown above. We need use below condition to display alert box

*ngIf="title.errors && (title.touched || title.dirty)"

Because we do not want to display alert message initially. We will show the alert message only if there is any error, as well as, a user has touched the control or user has changed the input. 

In this type or Validation style we are not controlling the border color of the control, that we can do by using the visual feedback validation style.

<div class="form-group">
            <label>Title</label>
            <input type="text" class="form-control" formControlName="title" [ngClass]="{'is-invalid': title.errors && (title.touched || title.dirty), 'is-valid':title.valid}"   >
            <div class="invalid-feedback" >
              <strong>Invalid Title</strong>
            </div>
</div>

This will show the visual feedback for invalid title input as below.

Validation Style : Visual Feedback

Bootstrap provides two classes namely, is-invalid and is-valid, using this two classes of bootstrap form validation you can change the border color of control as well as control the invalid-feedback division.

We are using [ngClass] directive, which is an attribute directive, this directive apply the style class to the control based on valid condition. For example,

[ngClass]="{'is-invalid': title.errors && (title.touched || title.dirty), 'is-valid':title.valid}"

This will apply is-invalid class when title.errors && (title.touched || title.dirty) is true, the same way it will apply is-valid if title.valid is true.

As shown in the above example,

  • is-invalid class makes control border color red, as well as it will show the invalid-feedback division.
  • is-valid class makes control border color green.

You can use either of the validation style, based on your requirement and UI strategy.

In above example, we have displayed only one message for both required and minlength validators.

we can also show different message based on Validators type.

We can do that by checking another property on our form control called errors.

errors is an object which has one entry per validator, the key is the name of the validator and if the value is not null then the validator is failing.

<div class="form-group">
            <label>Title</label>
            <input type="text" class="form-control" formControlName="title" [ngClass]="{'is-invalid': title.errors && (title.touched || title.dirty), 'is-valid':title.valid}"   >
            <div class="invalid-feedback" >
              <strong *ngIf="title.errors?.required">Title is required</strong>
              <strong *ngIf="title.errors?.minlength">Title length must be greated than 10 character. </strong>
            </div>
</div>

If an errors object has a key of required it means the control is failing because it’s required and the user hasn’t entered any value into the input field.

If we will see in errors property in deep, it also provides a useful information. we can see the other information of an errors object by using it in interpolation with JSON pipe. as shown below,

{{title.errors | json}}

For example, if title control is failing for minlength validator, title.errors give us minlength object with extra information as requiredLength and actualLength properties. as shown below

{
  "minlength": {
    "requiredLength": 8,
    "actualLength": 1
  }
}

We can use this information in our validation error message to give the user a bit more help in resolving the issue. As shown below 

<div class="form-group">
            <label>Title</label>
            <input type="text" class="form-control" formControlName="title" [ngClass]="{'is-invalid': title.errors && (title.touched || title.dirty), 'is-valid':title.valid}"   >
            <div class="invalid-feedback" >
              <strong *ngIf="title.errors?.required">Title is required</strong>
              <strong *ngIf="title.errors?.minlength">Title length must be greated than {{title.errors?.minlength.requiredLength}} character. 
                        {{title.errors?.minlength.requiredLength - title.errors?.minlength.actualLength}} more character required. </strong>
            </div>
</div>
Validator Specific Error Message

The same way we will add the validation message for other form controls.

The ? is called the elvis operator, it means: “Only try to call the property on the right of ? if the property on the left of ? is not null” 

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl} from '@angular/forms';
import { Validators } from '@angular/forms';
import { Product } from '../models/product';
@Component({
  selector: 'app-add-product-model',
  templateUrl: './add-product-model.component.html',
  styleUrls: ['./add-product-model.component.css']
})
export class AddProductModelComponent implements OnInit {

  productTypes = ['Laptop', 'Mobile'];

  myForm: FormGroup;

  title: FormControl;
  modelName: FormControl;
  color: FormControl;
  productType: FormControl;
  brand: FormControl;
  price: FormControl;


  constructor() { }

  ngOnInit() {

    this.title = new FormControl('', [Validators.required, Validators.minLength(10)]);
    this.modelName = new FormControl();
    this.color = new FormControl('', Validators.pattern('[a-zA-Z]*'));
    this.productType = new FormControl('', Validators.required);
    this.brand = new FormControl('', Validators.required);
    this.price = new FormControl('', [Validators.required, Validators.min(1)]);

    this.myForm = new FormGroup({
      'title': this.title,
      'modelName': this.modelName,
      'productType': this.productType,
      'color': this.color,
      'brand': this.brand,
      'price': this.price
    });

  }

}
<div class="card">
  <div class="card-body">
    <h4 class="card-title">ADD PRODUCT (Model Driven Form)</h4>
    <div class="row">
      <div class="col-md-6">
        <form [formGroup]="myForm" novalidate>
          <div class="form-group">
            <label>Title</label>
            <input type="text" class="form-control" formControlName="title" [ngClass]="{'is-invalid': title.errors && (title.touched || title.dirty), 'is-valid':title.valid}"   >
            <div class="invalid-feedback" >
              <strong *ngIf="title.errors?.required">Title is required</strong>
              <strong *ngIf="title.errors?.minlength">Title length must be greated than {{title.errors?.minlength.requiredLength}} character. {{title.errors?.minlength.requiredLength - title.errors?.minlength.actualLength}} more character required. </strong>
            </div>
          </div>
          <div class="form-group ">
            <label>Model Name</label>
            <input type="text" class="form-control" formControlName="modelName">
          </div>
          <div class="form-group ">
            <label>Color</label>
            <input type="text" class="form-control" formControlName="color" [ngClass]="{'is-invalid': color.errors && (color.touched || color.dirty)}">
            <div class="invalid-feedback" >
              <strong>Only Alphabets are allowed. </strong>   
            </div>
          </div>
          <div class="form-group ">
            <label>Product Type</label>
            <select class="form-control" formControlName="productType" [ngClass]="{'is-invalid': productType.errors && (productType.touched || productType.dirty), 'is-valid':productType.valid}" >
              <option *ngFor="let p of productTypes" [value]="p">{{p}}</option>
            </select>
            <div class="invalid-feedback" >
                <strong>Select Product Type.</strong>    
            </div>
          </div>
          <div class="form-group">
            <label>Brand</label>
            <input type="text" class="form-control" formControlName="brand" [ngClass]="{'is-invalid': brand.errors && (brand.touched || brand.dirty), 'is-valid':brand.valid}">
            <div class="invalid-feedback" >
                <strong>Brand Name is required.</strong>    
            </div>
          </div>
          <div class="form-group">
            <label>Price</label>
            <input type="number" class="form-control" formControlName="price" [ngClass]="{'is-invalid': price.errors && (price.touched || price.dirty), 'is-valid':price.valid}">
            <div class="invalid-feedback" >
                <strong *ngIf="price.errors?.required"> Price is required</strong>
                <strong *ngIf="price.errors?.min"> Price should be greated than zero.</strong>    
            </div>
          </div>
          <button type="submit" class="btn btn-primary">Submit</button>
          <button class="btn btn-default" >Reset</button>
        </form>
      </div>

      <div class="col-md-6">
        <label>
          <strong>myForm : JSON</strong>
        </label>
        <pre class="alert alert-dark">{{myForm.value | json}}</pre>
      </div>
    </div>  
  </div>
</div>

In this chapter, we have seen,

  • Overview of form validation.
  • Use of Validators for Form Validation
  • Form Control State: Dirty & Pristine, Touched & Untouched, Valid & Invalid
  • Validation Styling using Alert Component of Bootstrap and Visual Feedback
  • Validator Specific Error Message