Introduction

In the first part of this Angular 2 Forms series we’ve created a first form component in Angular 2. This was just a simple form consisting of the following input elements:

This form has been implemented by using the template-driven forms approach of Angular 2. This means that a component’s template was used to arrange the forms HTML elements. In addition Angular 2 form directives have been used in the template to enable the framework to construct the internal control model that implements form functionality. The following template code was used:

<div class="container"> <h1>Book Form:</h1> <form> <div> <label for="title">Title</label> <input type="text" class="form-control" id="title" required [(ngModel)]="model.title" name="title"> </div> <div> <label for="author">Author</label> <input type="text" class="form-control" id="author" required [(ngModel)]="model.author" name="author"> </div> <div> <label for="url">URL</label> <input type="text" class="form-control" id="url" required [(ngModel)]="model.url" name="url"> </div> <button type="submit" class="btn btn-default">Submit</button> </form> <div> <h2>Model:</h2> {{ currentBook }} </div> </div>

In this second part of the Angular 2 Forms series we’re going to focus on another important aspect of form creation: input validation. Angular 2 makes form validation very easy. In the following you’ll learn how to use apply form validation by using HTML validation attributes and Angular 2 validation functionality.

Adding HTML Validation Attributes To Input Elements

Form validation in Angular 2 is based on HTML validation attributes. HTML validation attribute are used in input elements. One validation attribute has already been applied to all three input elements of our form: required. The required attribute defines that entering a value in the input field is mandatory. A full list of HTML validation attributes can be found at https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation.

Now we’re going to add to more validation attributes to the first input field of the book title. Let’s define that we want the user to input a title which has a length between 5 and 30 characters:

<input type="text" class="form-control" id="title" required minlength="5" maxlength="30" [(ngModel)]="model.title" name="title">

To implement that constraint we’re adding the HTML validation attributes minlength and maxlength to the input element.

Furthermore we’re using the pattern HTML validation attribute to for the URL input field:

<input type="text" class="form-control" id="url" required pattern="https?://.+" [(ngModel)]="model.url" name="url">

Herewith we make sure that only valid URLs starting with http:// or https:// can be entered in this input field.

Adding Validation Error Messages To The Form

In the next step we’re going to include error messages in the form template. If a certain validation rule is not met these messages should be displayed to the user:

<div class="container"> <h1>Book Form:</h1> <form> <div class="form-group"> <label for="title">Title</label> <input type="text" class="form-control" id="title" required minlength="5" maxlength="30" [(ngModel)]="model.title" name="title" #title="ngModel"> <div *ngIf="title.errors && (title.dirty || title.touched)" class="alert alert-danger"> <div [hidden]="!title.errors.required"> Book title is required! </div> <div [hidden]="!title.errors.minlength"> Title must be at least 5 characters long. </div> <div [hidden]="!title.errors.maxlength"> Title cannot be more than 30 characters long. </div> </div> </div> <div class="form-group"> <label for="author">Author</label> <input type="text" class="form-control" id="author" required [(ngModel)]="model.author" name="author" #author="ngModel"> <div *ngIf="author.errors && (author.dirty || author.touched)" class="alert alert-danger"> <div [hidden]="!author.errors.required"> Book author is required! </div> </div> </div> <div class="form-group"> <label for="url">URL</label> <input type="text" class="form-control" id="url" required pattern="https?://.+" [(ngModel)]="model.url" name="url" #url="ngModel"> <div *ngIf="url.errors && (url.dirty || url.touched)" class="alert alert-danger"> <div [hidden]="!url.errors.required"> URL is required! </div> <div [hidden]="!url.errors.pattern"> Must be a valid URL! </div> </div> </div> <div> <button type="submit" class="btn btn-default">Submit</button> </div> </form> <div> <h2>Model:</h2> {{ currentBook }} </div> </div>

First of all notice that template variables have been introduced for all three input elements by adding

#title="ngModel" to the title input control

to the title input control #author="ngModel ” to the author input control

” to the author input control #url="ngModel" to the URL input control

By using the variables title, author and url in the code we now have access to the form controls. We are able to check if the form control is in an error state and display messages to the user.

To display error messages a div element is included for every input element

<div *ngIf="title.errors && (title.dirty || title.touched)" class="alert alert-danger"> ... </div>

NgIf is used to only display the content of this element if the assigned expression string is valid. The expression becomes valid if the control is in an error state (title.errors is true) and at the same time the control is marked is dirty (title.dirty is true) or is marked as touched (title.touch is true). This ensures the error messages are not displayed initially. If the dirty flag is set the value of the input element has been changed by the user. If the touched flag is set to true the control has been visited by the user.

For each error message another div block is placed inside of the previously described block:

<div [hidden]="!title.errors.required">

Book title is required!

</div>

The hidden attribute is bound to the negated value of the respective error. E.g. if title.errors.required is true (which means the field value is empty) the hidden attribute is set to false, so that the error message is displayed.

Using Angular 2 Validation CSS Classes

Angular 2 automatically attached CSS classes to the input elements depending on the state of the control. The following class names are used:

ng-touched : Control has been visited

: Control has been visited ng-untouched : Control has not been visited

: Control has not been visited ng-dirty : Control’s value has been changed

: Control’s value has been changed ng-pristine : Control’s value hasn’t been changed

: Control’s value hasn’t been changed ng-valid : Control’s value is valid

: Control’s value is valid ng-invalid : Control’s value isn’t valid

We can make use of those class by defining CSS styling which gives additional visual feedback to the user. Insert the following code into file book-form.component.css:

.ng-valid[required], .ng-valid.required { border-left: 5px solid #42A948; /* green */ } .ng-invalid:not(form) { border-left: 5px solid #a94442; /* red */ }

The result can be seen in the following:

First, a red border is display on the left side of the input controls, indicating that a value is missing. If the user starts typing the and the field constraints are fulfilled the color changes to green.

Form Validation

The validation logic we’ve implemented so far is specific for single input fields of the form. We’re able to extend the logic to also take into consideration the validation status of the complete form. The evaluate if a form is valid or invalid can be useful to e.g. control if the form can be submitted or not.

First let’s introduce a new template variable for the form itself:

<form #bookForm="ngForm">

With that code in place we’re able to retrieve the validity status of the form by using bookForm.form.valid. Only if all input controls of the form a valid the form becomes valid too.

The form validity status can now be used together with the disabled attribute of the form’s submit button:

<button type="submit" class="btn btn-default" [disabled]="!bookForm.form.valid">Submit</button>

Now the submit button is only enables of the form is in valid state.

The final result can be seen in the following: