We know that working with forms in Angular is just great. Due to its scope model nature, we always have a reference to the actual form state in its corresponding scope, which makes it easy to access particular field values or represent the form state in our views.
If there’s one thing that takes probably most of the work when building forms, it’s their validation. We know that validation on the server-side is always required in order to process given user data that could break our app. But we also want to provide a great user experience, which is where validation on the client-side comes into play. We already learned about ngModelOptions. In this article we are going to discuss the ways we’ve been able to validate data in our Angular forms in 1.2 and detail how version 1.3 makes it even easier with the validators pipeline.
TABLE OF CONTENTS
Built-in form validation
Before we start looking at what the latest bigger Angular release brings to the table when it comes to form validation, let’s take a look at what capabilities we had anyway and also especially, why there was a need for an improvement at all.
HTML5 provides some validation attributes we can use to let the browser validate our form controls. For example, if we want to have native validation support for email input fields, all we have to do is to apply the
required attribute to that element.
And as you probably know, there are a couple of other validation attributes like
pattern. However, it turns out that the API is inconsistent and not even supported in all browsers and platforms today. That’s why Angular provides basic implementation for HTML5 validation with its
ngModel directive and controller and makes it consistent between browsers.
Here’s a list of supported validation attributes:
In addition to that, Angular validates certain input types automatically without us doing anything. The following code displays a simple form that has just one field of type
ng-model to it makes Angular aware of it. Also notice the
name attribute of the form which publishes the
FormController instance of the form into the scope.
Running this code in the browser, you can see the validation happens automatically. Errors are even exposed on the
$error object, which makes displaying error messages a breeze.
number is used. In 1.3 there’s additional support for date and time inputs like
month as well.
Custom Validations - The Old Way
Built-in validations are nice, but in some cases we need validations that go far beyond the basic functionality we get out of the box. And this is where custom validations come in.
The key part of validation in Angular is the
ngModelController since it controls the logic of passing values back and forth between the DOM and the scope. In versions before 1.3, we were able to implement custom validations by using
ngModelController and it’s
Let’s say we want to implement a custom validation that checks if the value that is passed by the user is an actual integer. In order to do that, we would first create a new directive that accesses
ngModelController like so:
In a directive’s
link function, we can ask for other directives controllers with the
require property of the directive definition object.
ctrl is now a reference to an
ngModelController instance which has a
$parsers property. These two properties are arrays, that act as pipelines that get called when certain things happen.
These certain things are:
Model to view update - Whenever the bound model changes, all functions in
$formattersare called one by one, in order to format the value and changes it’s validity state.
View to model update - Whenever the user interacts with a form control, it calls the
$setViewValuemethod, which in turn calls all functions of the
$parsersarray in order to convert the value and also change it’s validity state accordingly.
Okay, so we have one pipeline that pipes the value from model to view and another one that pipes it from view to model. Since we want to check if the given value in our control is an actual integer, we need to use the pipeline that is executed when the view updates the model, which is the
All we have to do now, is to add a new function to
$parsers that performs the needed checks and sets the validity state with
$setValidity() accordingly. In order to make sure that our validation function is called first in the pipe, we use Array.prototype.unshift.
We check if the new values matches against our regular expression. If it matches we set the validity of
true and return
viewValue, so it can be passed to further parser functions.
In case it doesn’t match, we set it’s validity to
false, which also exposes an
integer member on the
$error object, so we can display error messages accordingly. We also return
undefined explicitly, in order to stop processing of the pipe.
We can then use it like every other directive:
As you can see, there’s a lot to take care of when writing custom validations. We need to know about the
$formatters pipeline. We also need to set a value’s validity state explicitly with
In addition to that, it turns out that due to the nature of HTML5 form validation, some input types may not expose the input value until the valid value is entered.
So how does Angular 1.3 a better job?
Angular 1.3 introduces yet another pipeline, the
$validators pipeline, which is rather used than
$formatters. Unlike parsers and formatters, the validators pipeline has access to both,
modelValue, since it’s called once
$formatters has been successfully run.
Another API difference is that
$validators is not an array, but an object with each member describing a validator. Let’s implement our
integer custom validation as part of the
As you can see, we no longer have to take care of calling
$setValidity(). Angular calls
$setValidity() internally, with the value that a validator returns, which is either
And of course, if a value is invalid, an
$error is exposed on the
With 1.3, Angular goes even a step futher and makes asynchronous validations possible. Just imagine the case you have an input field for a user name and whenever a user types in a name, you need to perform some validity checks on your server. The application needs to wait until the server responses.
That’s why there’s next to
$validators another validators object called
$asyncValidators. Asynchronous validators work pretty much like synchronous validators except that they are asynchronous and therefore promise based. Instead of returning
false, we return a promise that holds the state of an asynchronous code execution.
Here’s what it could look like:
Asynchronous validators are called, after synchronous validators have been successfully executed. While an asynchronous validator is running, a
$pending object will be exposed on the field’s
ngModelController. Flags like
$invalid are set to
undefined at this point.
We could display a loading message like this (notice the
name attribute applied to the field to expose it’s controller):
Okay, we now learned about
$asynchValidators, but does that mean our existing
$formatters won’t work anymore?
The answer is no. The validation pipeline has been added to the existing pipelines. It is basically there, so developers can explicitly distinguish between validations and parsing/formatting related functionality.
Also, as we learned, the validators pipeline has a slight simpler API. We don’t have to take care of setting
$setValidity() anymore. And we can finally do proper asynchronous validations.
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.
Two-way Data Binding in Angular
Two-way data binding was one of the main selling points of AngularJS. Since Angular, we can build directives that implement...
Custom Form Controls in Angular
Angular makes it very easy to create custom form controls. Read on to learn how to do it!
Reactive Forms in Angular
Angular allows us to build forms in a model-driven fashion. In this article we're going to discuss what that looks...
Template-driven Forms in Angular
In this article we discuss the template-driven forms in Angular and all the directives that come into play.
Custom Validators in Angular
Often, we need to add custom validation capabilities to our application's form. In this article we're going to explore how...
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...