In one of our articles of our blog series on exploring Angular 1.3, we’ve covered a very nice feature that makes validating forms in Angular a breeze. Right, I’m talking about the validators pipeline.
While the validators pipeline seems to make our life a lot easier and we as developers think it can’t get any better, it turns out there’s another bigger feature that adds even more awesomeness to the world of forms when building Angular applications: ngMessages.
ngMessages is an entire new module that comes with a couple of directives to enhance the support for displaying messages within templates. Which means, even if in this article we’re using it just for forms, we’re not restricted to do so. But let’s start right away and take a look at a scenario that
ngMessages tries to solve.
TABLE OF CONTENTS
Displaying messages in forms - The old way
Providing a good user experience is always important. When building forms, it’s pretty common to display messages to the user depending on the data that the user entered into the form fields. This, for example, could be a message that tells the user that a specific field is required to be filled out, or a message that says that the given data doesn’t match a certain pattern.
We’ve already learned about the validators pipeline that lets us easily determine if the value of a form field is valid or not. Each state of an input element is exposed on the associated scope of a form (as long as a
name attribute is applied), which makes it super easy to conditionally display DOM elements that have (validation) messages.
Let’s say we want to build a common login form where the user needs to enter an email address and a password, like this:
name attributes on the
<input> elements. These make sure the form’s
FormController instance, that holds the state of the form, is exposed on the scope. Giving the
<input> elements names exposes their state on the
FormController. In other words,
loginForm is now an actual expressions on the scope that we can use to evaluate data in our template.
We also specify the
type of each
<input> which adds some default validations to the fields behind the scenes. In fact, we can visualize the form’s state by adding the following expression to our document:
While entering an email address, Angular automatically validates the data given to the field and exposes the state to
loginForm. That means, as long as we’re entering data which isn’t valid, the
$error property of
loginForm gets extended with a new object
In addition to that, due to adding
name attributes to the field itself, there’s also an
password property on
loginForm which have an
$error object themselves. The
$error object on form fields is a simple key/value store that represents the error state for each applied validator on a field.
So, in order to display a message when there’s an error with the default email validation (which we get automatically by specifying the
type), all we need to do is to conditionally add a DOM node to the document like this:
loginForm.email is the field reference,
$error.email is the result of the
required attribute to the field, which also adds a validator to the field behind the scenes.
Displaying an error message accordingly could look like this:
I think we get the idea. Now imagine instead of just two different validations, we have five validations for just one field and we want to display a message for each. The markup for our form gets out of control very quickly. Here’s how our
password field could be extended with conditional messages.
We probably also want to control what message shows up when, especially when multiple messages occur at the same time. Try to build that with just
ngIf directives all over the place. And this is where
ngMessages comes into play.
Displaying messages in forms with
ngMessages comes as a separate module. In order to use it, we first need to install it. One way to do so is to use npm:
Then, we need to embed the actual script in our document:
Once done, we declare
ngMessages as module dependency of our app and we are ready to go.
Alright. The module is now installed and ready to be used. Let’s take a look at what our login form would look when
ngMessages is used. The module comes with two directives -
ngMessages directive gets an expression that evaluates to an object where each member can control if a certain message is displayed or not,
ngMessage directive is in charge of displaying that particular message.
Things are a bit easier to understand when actual code is shown, so here’s what our DOM looks like when
ngMessages is used:
We have an element where
ngMessages directive is applied. As mentioned earlier,
ngMessages gets an expression that evaluates to an object where each member is either
false. This fits perfectly to what
FormController exposes on the scope, when fields have validations errors and a
name attribute applied.
ngMessage directive, we can just conditionally display messages by providing it with a name of a validator that is applied to the corresponding form field. Now, whenever a validator declares the value of the password field invalid, it displays the message that belongs to it.
In case we don’t want to pollute our DOM with additional elements, just to apply the directives,
ngMessage are not restricted to attributes. We can also use them as elements. In that case,
when attributes are needed to pass expressions accordingly.
Taking a closer look at this code snippet, you might think this is a very familiar construct. Right. It looks pretty much like using
ngSwitchWhen directives. In fact, it is almost the same.
So what is the difference and why do we want to use one version over the other? Well, it turns out
ngMessages is much more powerful.
Prioritization and multiple messages
Only one message is displayed at a time by default when using
ngMessages. However, there might be cases where we want to display multiple message for a single field at a time. This we cannot do with
ngSwitch, since it only renders a single match of the given construct. In order to display multiple messages, we can apply the
ng-messages-multiple attribute to our
ngMessages directive. This causes all messages to be displayed where the corresponding validations fail.
Or, if we want to display a single one, we at least want to prioritize which message shows up when. For example, before we want to display a message that says the given value doesn’t match a particular pattern, we first want to make sure we have at least six characters (
minlength) and therefore displaying a message for that first.
We can do so by simply assembling our DOM accordingly. Messages that appear first in the DOM are also displayed first. The following construct displays a message for entering a value that is too short, before it informs the user that the given value doesn’t match certain pattern:
Reusing existing messages
It gets even better. It’s pretty common to have the same messages for the same validations across many forms. Instead of redefining the same message over and over again to have it available in each and every form,
ngMessages directive expects yet another optional attribute called
ng-messages-include that lets us include predefined messages at different places in our application.
All we need to do is to define a template that contains the messages we want to reuse and give it an id so we can reference it via
If the template is not present in the document, Angular performs a
$templateRequest to fetch the template first.
Now we’ve learned that how we can define templates in order to reuse messages at different places in our application. You might think that this is a scenario where HTML Templates would be a better fit, instead of doing script overloading. I agree on that, since this is what the
<template> element has been designed for. Unfortunately, at the time of writing this article, this was not supported, which is why I’ve created a corresponding issue here.
There’s a lot more to cover and I recommend heading over to the official docs to learn everything you need to know. This module is not only a time-saver but also adds some very powerful features to our declarative world.
Angular Master Class at Shopware
Join our upcoming public training!Get a ticket →
Get updates on new articles and trainings.
Join over 1400 other developers who get our content first.
Futuristic Routing in Angular
An this article we discuss the new router APIs and how it's going to change the way we implement component...
Go fast with $applyAsync in Angular 1.3
Angular 1.3 comes with a feature to share a running $digest cycle across multiple XHR calls. This articles details how...
Validators Pipeline in Angular 1.3
In this article we discuss a newly introduced feature called custom validators, so we don't have to hijack parsers and...
Binding to Directive Controllers in Angular 1.3
In this article we are going to take a look how to bind values to directive controllers to make them...
Disabling Debug Info in Angular 1.3
This article details how to give your app a performance boost in production environments with just a single line of...
ES6 Style Promises in Angular 1.3
Angular 1.3 starts streamlining its promise APIs with the ES2015 standard. This article details what these additions mean to us....