Angular 4 Template Driven Forms : Building and Validating Forms

Nowadays all kinds of websites are using HTML Forms or Web Forms. Web Forms are used to collect the data from the user and send it to the server for processing. And web forms generally have buttons, check boxes, text inputs, and radio buttons, etc to collect different types of user data. For more check forms.

In our article, we will see how to handle forms and learn performing validations using Angular Forms API.

Angular 4 forms are two types: 1. template-driven forms 2. Reactive forms. Now you’ll learn to build an Angular form with template-driven forms and next article we can discuss reactive forms.

angular 4

Angular 4 forms use the following statuses:

  • touched or untouched – Shows the visiting state of the controls.
  • valid or invalid – Shows the controls validity.
  • pristine or dirty – Cleanness of the controls.

Let’s see the sample form:


<form>

 <div>

   <label>Name</label>

   <input type="text" name="name"/>

 </div>

 <div>

   <label>Email</label>

   <input type="email" name="email"/>

 </div>

 <div>

   <label>Age</label>

   <input type="text" name="age"/>

 </div>

 <div>

   <h3>Address</h3>

   <div>

     <label>City</label>

     <input type="text" name="city"/>

   </div>

   <div>

     <label>Country</label>

     <input type="text" name="country"/>

   </div>

 </div>

 <button type="submit">Submit</button>

</form>


The requirements for the above form is as follows:

Name: A unique name should be provided.

Email: A valid email id that is unique is required.

Age: Age must be a number between 18 and 85.

Country: A valid country name is required.

Once we fill all the fields with valid inputs, the submit button will be enabled. On click, it will submit the form.

In the next steps, we will learn to implement the above specifications.

Template-driven Forms

These are mostly same as the forms in Angular 1. It uses the models for the driven approach. So, Template Drive Forms uses the Angular directives in the template to handle the forms.

Angular has different type of modules for different type of specific actions. We must choose proper module according to our usage and should import in “AppModule”.


import {BrowserModule} from '@angular/platform-browser' // to provide essential service to run app

import {NgModule} from '@angular/core'

import {FormsModule} from '@angular/forms' // will active template driven
import {AppComponent} from 'src/app.component'; // importing root component
@NgModule({

 imports: [ BrowserModule, FormsModule ],

 declarations: [ AppComponent], // declaring AppComponent to run component.

 bootstrap: [ AppComponent ]

})

export class AppModule {}


In the above code, we have imported the BrowserModule which provides the service for launching and running angular applications in the browser. For activating the template-driven we must import FormsModule. And most importantly we must import and declare root component “AppComponent”. You can activate the Template driven using the above code.

AppComponent (app.component.ts) should look likbelow.


import {Component} from '@angular/core'
@Component({

selector: 'my-app',

 templateUrl: 'src/app.component.html'

})

export class AppComponent {
}

The form code should be copied in the component template app.component.html.

You can notice that all the elements have “name” attribute which will be used to identify properly. FormsModule will detect a form element and ngForm automatically. To register the input elements to our ngForm we should include ngModel for all elements. This looks like below:


<form>

 ..

 <input type="text" name="name" ngModel />

 ..

 <input type="text" name="email" ngModel />

 ..

 <input type="text" name="age" ngModel />

 ..

 <input type="text" name="country" ngModel />

 ..

 <input type="text" name="city" ngModel />

</form>

NgForm offers you two functionalities:

  • Retrieving all the registered controls values.
  • Retrieving the complete state of all the controls.

To attach the ngForm to Form you need to include the below code.


<form #validateForm="ngForm">

 ..

</form>


After attaching the ngForm you can access the form controls using 
validateForm.value.

In above HTML form, you can notice that address has a sub-group with country and city as fields. Don’t worry Angular 4 provides us “ngModelGroup” directives to separate the groups. 


<form #validateForm="ngForm">

 ..

 <div ngModelGroup="address">

   ..

 </div>

   ..

</form>

So far we have ngForm with HTML form controls. Now we are going to see how to handle submit and validations. To handle a submit we have “ngSubmit”. ngSubmit will do the same thing that is done by onSubmit and with passing the form name to the ngSubmit function we can get the handler of the submitted of in component.


<form #validateForm="ngForm" (ngSubmit)="validate(validateForm)">

 …

</form>

From the above example, you can see function “validate” method. So we must create a “validate” method in AppComponent like below:


validate (validateForm: NgForm) {

 console.log('Successfully Submitted');

 console.log(validateForm);

}

In this way, you can access the NgForm component, when the form is submitted.

In another way, you can use @ViewChild decorator property in the component. Like:


@ViewChild(‘validateForm’)

private validateForm: NgForm;

Great, Now that we have created perfect Angular 4 Form. Now are going to assign validation for the form inputs. With this, you can access the form from component and check if the submit event was triggered or not.

Validation:

Validation is used to ensure that the user enters the valid data. Validation is most important for every form in the web application to ensure whether the user is entering the valid data to work with and we should provide a meaningful and understandable message so that the user will enter the data that is valid. 

Angular 4 provides some common validations like : required, email, maxLength, minLength, etc … It is very easy to add validator for form controls just add validator directive to form controls. It look slike below:


<input name="email" ngModel required/>

Using the above example, now edit the form as following.


<form #validateForm="ngForm" (ngSubmit)="actionOnSubmit(validateForm)" novalidate>

 <p>Is "validateForm" valid? {{validateForm.valid}}</p>

 ..

 <input type="text" name="name" ngModel required/>

 ..

 <input type="text" name="email" ngModel required/>

 ..

 <input type="text" name="age" ngModel required pattern="\\d{2,2}"/>

 ..

 <div ngModelGroup="address">

   ..

   <input type="text" name="country" ngModel required/>

   ..

   <input type="text" name="city" ngModel/>

 </div>

 ..

</form>

The “novalidate” in the form element will disable the default HTML5 browser validation.

Here we have to assign required validation for the name, email, age, and country inside address. We have to print the form valid status using {{validateForm.valid}}. This will print true if the form is valid.

We can write a custom helper component to modify and the show errors.


// errors.component.ts

import { Component, Input } from '@angular/core';

import { AbstractControlDirective, AbstractControl } from '@angular/forms'; // Here we have importing AbstractControlDirective and AbstractControl to get access of control and create and handle directive.
@Component({

selector: 'errors', // Define selector for show error in front-end.

template: `

  <ul *ngIf="showErrors()">

    <li style="color: red" *ngFor="let error of errors()">{{error}}</li>

  </ul>

`, // Defining template how error shown for form element.

})

export class ErrorsComponent {
private static readonly messages = {

  'required': () => 'Required', //Required validation error

  'minlength': (params) => 'The min number of characters is ' + params.requiredLength, //minimum length validation error

  'maxlength': (params) => 'The max allowed number of characters is ' + params.requiredLength, //maximum length validation error

  'pattern': (params) => 'The required pattern is: ' + params.requiredPattern, //pattern matching validation error,

  ‘age’: (params) => params.message // It custom validator method will learn this below steps.

};
@Input()

private control: AbstractControlDirective | AbstractControl;
showErrors(): boolean {

  return this.control &&

    this.control.errors &&

    (this.control.dirty || this.control.touched);

}
errors(): string[] {

  return Object.keys(this.control.errors)

    .map(field => this.getMessage(field, this.control.errors[field]));

}
private getMessage(type: string, params: any) {

  return ErrorsComponent.messages[type](params);

}
}

The above component shows an error if controls have error or when the input controls are touched or dirty. All messages will be listed from the messages for displaying message in the frontend. we have to add component selector element in the template for each control like the following.

 


<div>

 <label>Age</label>

 <input type="text" name="age" #age="ngModel" ngModel required pattern="\\d{2,2}"/>

 <errors [control]="age"></show-errors>

</div>

As discussed before age should be between 18-85 and if the country is USA the city should be “New York”. To achieve this we need to write custom validations. This is difficult to handle in JavaScript but actually  Angular 4 offers a interface called “Validator”. With help of Validator we can write custom validations easily. Surprised yes me too. You can use the Validator as below.

 


@Directive({

 selector: '[validator]',

 providers: [{provide: NG_VALIDATORS, useExisting: ValidatorDirective, multi: true}]

})

class ValidatorDirective implements Validator {

 validate(c: Control): {[key: string]: any} {

   return {"custom": true};

 }

}

While implementing “Validator” you should overwrite “validate” method. The validate method will handle input and the expected output. “validate” method will accept AbstractControl as a parameter which will accept FormGroup, FormControl and FormArray as values. And the output of the validate method can be null or undefined or ValidationErrors object if user enters invalid data.

Below you can see the custom validator for age and country.

 


import { Directive } from '@angular/core'; //  Will import the angular core features. Required for all components , modules, etc...

import { NG_VALIDATORS, FormControl, Validator, ValidationErrors } from '@angular/forms'; // Will import the angular forms
@Directive({

selector: '[age-validate]',

providers: [{provide: NG_VALIDATORS, useExisting: AgeValidatorDirective, multi: true}]

})

export class AgeValidatorDirective implements Validator { // Creating class implementing Validator interface
validate(c: FormControl): ValidationErrors {

  const num = Number(c.value);

  const isValid = !isNaN(num) && num >= 18 && num <= 85;

  const message = {

    'age': {

      'message': 'The age must be a valid number between 18 and 85' // Will changes the error defined in errors helper.

    }

  };

  return isValid ? null : message;

}

}

 

In the above code, At first we have created AgeValidatorDirective class implementing Validator interface and we are checking whether the age is between 18 and 85 in validate method and return the different state based on age conditions. It will return null if the user data is not valid, else it will return an error message. And we have declared age-validate directive in “providers” in @Directive to act as the directive. And don’t forget to declare in AppModule. You can see how to use in inputs,

 


<input type="text" name="age" #age="ngModel" ngModel required age-validate/>

Above code will bind the custom validation to the field.

 


<button [disabled]="!myForm.valid">Submit</button>

The above code will disable the “Submit” button if the user enters invalid information. Once the user enters valid information button will automatically get enabled by ngModel.

 

The “validate” function we have written in app.component.js will return the values with which you can save user entered data to SQL database or NoSQL database.

 


validate (validateForm: NgForm) {

 console.log('Successfully Submitted');

 console.log(validateForm.value);

}

You can access the full form controls by using “validateForm” and validateForm. The value will return user enter data as JSON object. You can access specific data using DOT operator like “validateForm.value.name” with this we can collect data and manage it.

 

In the above example, we have learned about template-driven forms and how to work with template-driven forms. Basically, it’s very similar to the AngularJS forms so it’s very easy to integrate validation and we can integrate with minimal program changes. Here (https://github.com/agiratech/angular4-template-driven-forms.git) you can find the repo for example we discussed. In my next article, we will see about “Reactive Forms” with the same example. Follow Agiratechnologies to learn more about Web Development.

Happy Coding…