AngularJS Form Validation with NgMessages

Chris Sevilleja

Angular has always strived to provide tools to make forms easier. Just using Angular to submit AJAX forms and using Angular's built-in validation makes using forms that much easier.

There are even great 3rd party modules to help us work with forms like angular-formly.

Since Angular 1.3, there is a new tool to creating and managing forms in AngularJS, ngMessages. This module specifically helps us deal with displaying error messages from form validation.

Validation Messages without ngMessages

Let's take a quick look at how forms look without using this module.


<form name="userForm">

    <input 
        type="text" 
        name="username" 
        ng-model="user.username" 
        ng-minlength="3" 
        ng-maxlength="8"
        required>

    <!-- show an error if username is too short -->
    <p ng-show="userForm.username.$error.minlength">Username is too short.</p>

    <!-- show an error if username is too long -->
    <p ng-show="userForm.username.$error.maxlength">Username is too long.</p>

    <!-- show an error if this isn't filled in -->
    <p ng-show="userForm.username.$error.required">Your username is required.</p>

</form>

We are explicitly showing each error message only if that error exists. This can get tedious when we have multiple errors that we want to show.

This is where ngMessages comes in. This module brings some sanity to validation messages.

A Quick Look at ngMessages

Let's take the above example and see how that would look in ngMessages.


<form name="userForm">

    <input 
        type="text" 
        name="username" 
        ng-model="user.username" 
        ng-minlength="3" 
        ng-maxlength="8"
        required>

    <div ng-messages="userForm.name.$error">
        <p ng-message="minlength">Your name is too short.</p>
        <p ng-message="maxlength">Your name is too long.</p>
        <p ng-message="required">Your name is required.</p>
    </div>

</form>

Much simpler! ngMessages will handle showing and hiding specific messages based on the errors. ngMessages is basically looping through the userForm.name.$errors object and displaying messages based on that.

Using ngMessages

The setup for ngMessages is very simple. We just need to load the module after Angular and then inject it into our application.

Load Dependencies and Inject


<!-- load angular -->
<script src="//code.angularjs.org/1.4.0/angular.js"></script>

<!-- load ngmessages -->
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular-messages.js"></script>

<!-- load our custom app -->
<script src="app.js"></script>

Now we can inject into our application in the app.js file.


angular.module('app', ['ngMessages']);

Show Messages

Just use the ng-messages directive and pass in the field you want and its $error object.

This is the format:


<div ng-messages="<formName>.<inputName>.$error">
    <p ng-message="<validationName>">Your message here.</p>
</div>

A Sample App

Let's create a simple sample application to demonstrate ngMessages in practice. We're working from a Plunkr. You can create a new one for yourself or just follow along in the code provided.

The HTML

We're going to be using some very simple HTML here. We just need a form after all.

Here's our index.html file:


<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ngMessages Demo</title>

    <!-- CSS -->
    <!-- load bootstrap and add some custom css -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootswatch/3.3.4/cerulean/bootstrap.min.css">
    <style>body { padding-top:50px; }</style>

    <!-- JS -->
    <script src="//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 class="container" ng-app="app" ng-controller="MainCtrl as main">

    <!-- create our form -->
    <form name="userForm" novalidate>

        <!-- name field -->
        <div class="form-group">
            <label>Name</label>
            <input type="text" name="name" class="form-control" 
                ng-model="main.name"
                ng-minlength="5"
                ng-maxlength="10"
                required>

            <!-- ngMessages goes here -->
        </div>

        <!-- email field -->
        <div class="form-group">
            <label>Email</label>
            <input type="email" name="email" class="form-control" 
                ng-model="main.email"
                ng-minlength="5"
                ng-maxlength="20"
                required>

            <!-- ngMessages goes here -->
        </div>

        <div class="form-group">
            <button type="submit" class="btn btn-danger">Submit</button>
        </div>

    </form>

</body>
</html>

This will be the starting template for our app. We are using novalidate on our form so that we disable the HTML5 validations. We have our own validations and they look much better.

Our Angular App

We've started our HTML. Now we just need to create the Angular application that we already referenced in app.js, ng-app and ng-controller.

Create an app.js file and use the following:


angular
    .module('app', ['ngMessages'])
    .controller('MainCtrl', MainCtrl);

function MainCtrl() {}

We don't need to have anything in our controller right now, but this is where you would process your form.

ngMessages for name

The name field only has three validations (minlength, maxlength, required).

Here are the ng-messages for this input:


<div class="help-block" ng-messages="userForm.name.$error" ng-if="userForm.name.$touched">
    <p ng-message="minlength">Your name is too short.</p>
    <p ng-message="maxlength">Your name is too long.</p>
    <p ng-message="required">Your name is required.</p>
</div>

ngMessages for email

For our email input, let's take a different approach. If our form has multiple fields, then it can be tedious to create multiple ng-messages blocks. ngMessages gives us the ability to pull messages from an external file.

This means we can reuse the same messages for multiple fields!

Reusable ngMessages with File

Let's create a new file called messages.html. We can place all of our messages in this file and just call it with ng-messages-include.

Here's the messages.html file:


<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="required">This field is required</p>
<p ng-message="email">This needs to be a valid email</p>

Now we can use it for our email input:


<div class="help-block" ng-messages="userForm.email.$error">
    <div ng-messages-include="messages.html"></div>
</div>

This makes ngMessages very powerful and reusable.

Only Showing Messages After Blur

Let's say we wanted to only show error messages after a user has clicked out of the input that they are typing into. It isn't very intuitive to show errors even before a user has used an input.

Angular provides a simple way to do this. We just have to use ngShow and the $touched validation feature Angular provides.

For example, we can only show errors for the name input using the following:


<div class="help-block" ng-messages="userForm.name.$error" ng-show="userForm.name.$touched">
    ...
</div>

Now these messages will only show after an input blur.

Adding Bootstrap Error Classes

We also want to use the Bootstrap provided classes (.has-error) to highlight the field as red if there is an error. We can use ngClass to add the error class if the field is $invalid.


<div class="form-group" ng-class="{ 'has-error': userForm.name.$touched && userForm.name.$invalid }">

We are adding the .has-error class if this field has been $touched and $invalid.

Conclusion

With this simple module, Angular form validation has gotten that much easier. Try it out in your own applications and let us know how you like it. Do you prefer ngMessages, a third-party software like angular-formly or doing validation from scratch?

Chris Sevilleja

Co-founder of Scotch.io. Slapping the keyboard until something good happens.