In the previous article, we have seen Angular Form Validation using HTML5 attributes, Form State Properties, and Angular directives such as ng-minlength, ng-maxlength. As we could see this approach is simpler and easy but not efficient/flexible in case of multiple messages for a single field.
With the HTML5/form properties/directives approach, each error message is shown explicitly only if that error occurs. It means, for each error message to be displayed, the condition needs to be applied. This becomes tedious in case of multiple errors. This is where Angular Ng-Messages is introduced.
The ngMessages module provides enhanced support for displaying error messages within templates (when rendering message objects with key/value objects). Instead of using Javascript code or ng-If/ng-Show statements within your forms to show/hide the error messages specific to the state of the input field, the ngMessages and ngMessage directives are designed to handle the complexity and priority sequencing of the messages.
ngMessages:
What is ngMessage?
ngMessage is a directive (a component of the ngMessages module) with the purpose to show and hide a particular message.
For ngMessage to operate, a parent ngMessages directive on a parent DOM element must be situated since it determines which messages are visible based on the state of the provided key-value pairs that ngMessages listens on.
Note: ngMessageExp is similar to ngMessage, but instead of the static value, it accepts an expression to be evaluated for the message key.
The ngMessages directive is a collection of the key-value pair where each key needs to be associated with the child element (ngMessage) which will then show or hide the error message based on the truthiness of the key’s value in the collection. Basically, ngMessages is used to display the error messages for an input field using its $error object exposed by the ngModel directive.
The child element of the ngMessages directive is matched with the keys in the ngMessages keys collection by the ngMessage or ngMessageExp directive. On matching, if the value of the key in the collection is true, that particular error message is shown.
How to use ngMessages?
ngMessages is a module that can hold multiple child elements, which can be expressed as a collection of ngMessage. ngMessages directive can be used as an “Attribute” or as an “Element”. Let us look at the syntax of both as follows.
As an Attribute:
<ANY ng-messages="expression" role="alert">
<ANY ng-message="stringValue">Error Message</ANY>
<ANY ng-message="stringValue1, stringValue2, ...">Error Message</ANY>
<ANY ng-message-exp="expressionValue">Error Message</ANY>
</ANY>
As an Element:
<ng-messages for="expression" role="alert">
<ng-message when="stringValue">Error Message</ng-message>
<ng-message when="stringValue1, stringValue2, ...">Error Message</ng-message>
<ng-message when-exp="expressionValue">Error Message</ng-message>
</ng-messages>
Let us take a look at the below example to understand how to use the ngMessages directive. We have the input of type text called Name. This input must be entered by the user but the length should be between 3 and 8 characters. So here validations to be applied are: required, minlength, and maxlength.
<form name="loginForm">
<!--USERNAME -->
<div class="form-group">
<label>Name*</label>
<input type="text" name="name" class="form-control" ng-model="name" ng-minlength="3" ng-maxlength="8" required>
<div ng-messages="loginForm.name.$error" role="alert">
<div ng-message="required">Please enter a value for this field.</div>
<div ng-message="minlength">name is too short.</div>
<div ng-message="maxlength">name is too long.</div>
</div>
</div>
</form>
As you can see in the above code, we have ngMessages evaluated to $error object of ‘name’ and 3 ngMessage directives evaluated for each one of the validation keys.
Recommended Read:
Prioritizing the sequence/order of Messages:
By default, ngMessages will display only one message at a time for a particular key-value collection. If more than one message key in a collection is true at the same time, then which message to be shown is determined by the order of messages in the HTML template code. It means the first-placed message is prioritized as first.
As you can see in the above example, if all three message keys are true then the required will be displayed first. As you enter a character required message will disappear and the minlength message will be displayed. And similarly, after minlength disappears, the maxlength message will be displayed.
Display multiple messages at the same time:
Since ngMessages allows only one message element at a time, ng-messages-multiple or multiple attributes can be applied to the ngMessages container element to cause it to display applicable multiple message elements at the same time.
<div ng-messages="loginForm.name.$error" ng-messages-multiple> //to do </div>
OR
<ng-messages for="loginForm.name.$error" multiple> //to do </ng-messages>
Reuse of Messages:
In addition to prioritization, ngMessages supports Message Reusability. Here messages can be reused by defining the messages in a separate file or in an inline template and referring it to the ngMessages. This allows the generic collection of messages to be reused across any part of the application.
This reusability is achieved using the ng-messages-include directive.
<script type="text/ng-template" id="error-messages">
<div ng-message="required">This field is required</div>
<div ng-message="minlength">This field is too short</div>
</script>
<div ng-messages="loginForm.name.$error" role="alert">
<div ng-messages-include="error-messages"></div>
</div>
Sometimes this generic message collection may not be useful enough to match all input fields. So ngMessages provides the ability to override the message defined in the remote template by redefining the messages in the container directive.
<!-- a generic template of error messages known as "my-custom-messages" -->
<script type="text/ng-template" id="my-custom-messages">
<div ng-message="required">This field is required</div>
<div ng-message="minlength">This field is too short</div></script>
<form name="loginForm">
<label> Name
<input type="text" name="name" ng-model="name" minlength="3" maxlength="8" required />
</label>
<!-- any ng-message elements that appear BEFORE the ng-messages-include will
override the messages present in the ng-messages-include template -->
<div ng-messages="loginForm.name.$error" role="alert">
<!-- this required message has overridden the template message -->
<div ng-message="required">You did not enter your email address</div>
<!-- this is a brand new message and will appear last in the prioritization -->
<div ng-message="maxlength">Your name is too long</div>
<!-- and here are the generic error messages -->
<div ng-messages-include="my-custom-messages"></div>
</div>
</form>
A simple example using ngMessages:
We will take a simple login form and we will implement the form validation in AngularJS with ngMessages:
As explained in the last article, Form Validation in AngularJS, we will use the same login page with three fields: Name, Username (should be an email of the user), and Password. We shall perform the same validations using ngMessages. So validation requirements are like,
- Name field is required
- Name field length must be between 3 and 8.
- Username is required and should be a valid email.
- The password should be in length between 6 and 8 characters.
For this application, we will require 3 files: index.html and angular app app.js. We need a reusable message template i.e. messages.html
index.html
<!DOCTYPE html>
<html>
<head>
<!-- CSS -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css" />
<!-- JS -->
<script src="https://code.angularjs.org/1.4.0/angular.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular-messages.js"></script>
<script src="app.js"></script>
</head>
<body ng-app="validationApp" ng-controller="mainController as m">
<div class="container" style="width:600px">
<!-- PAGE HEADER -->
<div class="page-header">
<h3>AngularJS Form Validation using ngMessages</h3>
</div>
<!-- pass in the variable if our form is valid or invalid -->
<form name="loginForm" novalidate>
<!-- NAME -->
<div class="form-group" ng-class="{ 'has-error' :
loginForm.myName.$touched && loginForm.myName.$invalid }">
<label>Name*</label>
<input type="text" name="myName" class="form-control"
ng-model="m.name" ng-minlength="3" ng-maxlength="8" required>
<div class="help-block" ng-messages="loginForm.myName.$error"
ng-if="loginForm.myName.$touched">
<p ng-message="required">Your name is required.</p>
<p ng-message="minlength">Your name is too short.</p>
<p ng-message="maxlength">Your name is too long.</p>
</div>
</div>
<!-- USERNAME -->
<div class="form-group" ng-class="{ 'has-error' :
loginForm.userName.$touched && loginForm.userName.$invalid }">
<label>UserName(email)*</label>
<input type="email" name="userName" class="form-control"
ng-model="m.userName" required>
<div class="help-block" ng-messages="loginForm.userName.$error"
ng-if="loginForm.userName.$touched">
<p ng-message="email">Please enter a valid email.</p>
</div>
</div>
<div class="form-group" ng-class="{ 'has-error' :
loginForm.password.$touched && loginForm.password.$invalid }">
<label>Password*</label>
<input type="password" name="password" class="form-control"
ng-model="m.password" ng-minlength="6" ng-maxlength="8"
required>
<div class="help-block" ng-messages="loginForm.password.$error"
ng-if="loginForm.password.$touched">
<div ng-messages-include="messages.html"></div>
</div>
</div>
<button type="submit" class="btn btn-primary"
ng-disabled="loginForm.$invalid">
Submit
</button>
</form>
</div>
</body>
</html>
As you can see in the above code, we are using the ngMessages directive for handling validations. ngMessage directive will display the message according to the true value of its particular key present in the key collection of ngMessages. Also, we are using a remote template for defining message keys and referring to the same on the index page. This is done to show the reusability of messages. The submit button is disabled if the form is invalid. When all the validations get successful, the button gets enabled.
Now let us create an angular app and controller page i.e. app.js
// create angular app - app.js
var validationApp = angular.module('validationApp', ['ngMessages']);
// create angular controller
validationApp.controller('mainController', function($scope) {
});
Next, we will have the message keys template file i.e. messages.html
<p ng-message="required">This field is required</p>
<p ng-message="minlength">This field is too short</p>
<p ng-message="maxlength">This field is too long</p>
<p ng-message="email">This needs to be a valid email</p>
<p ng-message="password">This needs to be a valid password</p>
This file is just the collection of messages defined using the ngMessage directive. To use messages defined in this file, the ng-messages-include attribute allows us to refer to this file. You can see it on the index page for the password field. When the key in the input definition matches with the key in the template file and based on its truthness, the message is shown.
Let us take a look at the output of this application:
1) Initially when the app is loaded.
2) Validations for the Name field. When the Name field is simply touched but not entered and if the user moves to the next field, the below error is shown.
3) Text length validation. When the user starts entering the Name field, based on the length of the value, it displays a minimum length message or maximum length message as shown below.
As defined, the name should be between 3 to 8 characters in length. Since entered value is of 2 characters below error is displayed. If it exceeds the max length, “Your name is too long” message will be shown.
4) Highlighting a field. When the user doesn’t enter any value in the UserName field and goes off the focus of the field, it shows highlighted border (meaning required). As we do not have the error message defined as the required key, just the border is highlighted.
5) Email validation. As the user starts entering a value in the Username field, if it does not meet a valid email type, it displays the below-shown error. A valid email should have an ‘@’ character followed by other characters.
6) When the user fills in all the fields and if they pass all the validations, the Submit button gets enabled.
Conclusion:
NgMessages provides a simple module for error handling in AngularJS as compared to ng-If or ng-show. This eases a developer’s life by reducing the efforts as it supports reusability and prioritization.