We all have to get input from users, validate it and act on it. But everywhere I look I see developers that aren’t using Angular’s form capabilities to their full potential.
Yes, it requires learning the right buttons to push. Let’s spend a few minutes getting to know Angular forms. You’ll be surprised how easy it is to implement most of your forms’ UX.
In this post we’ll go through the steps of setting up a sign up form. We will implement a custom password validator and an asynchronous check that the supplied email address hasn’t been used yet.
The raw form
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
This should look familiar. We have a simple form with 2 fields and a nice button. Some things to note:
- We’re adding a
name
attribute to the form and inputs so they’ll be bound to our controller. We will use this shortly below. - Specifying
required
andtype=email
already gives us basic validation by Angular. But since some browsers have validation too we use thenovalidate
attribute so it won’t clash with Angular’s stuff. - Use
ng-submit
. You should always either pickng-submit
or haveng-click
on the submit button, never both. I preferng-submit
since I consider this action to be at the form’s level.
Prevent submitting an invalid form
This one’s an easy peasy. Just use ng-disabled
to prevent our form from getting submitted when the state of the form is invalid:
1 2 3 |
|
Angular Weirdness Alert: It’s important to use !vm.form.$valid
and not vm.form.$invalid
here. An Angular form can be in a third state, pending, which we’ll talk about soon. This means that $valid != !$invalid
. Le sigh. We don’t want to allow the form to submit in any state other than the valid state.
Playing with the form would now prevent us from submitting it if we don’t supply a value in both inputs.
Add password validation
Now we’re getting serious. Let’s add some basic validation: passwords should have at least 8 characters and contain both letters and numbers. We’ll use the awesome validators introduced in 1.3+. We’ll use a little directive that we’ll then attach to the password element:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
And add that directive to the element:
1 2 3 |
|
We’re placing our directive on the same input element with an ng-model
and so we can gain access to ngModelController
and add our validator. As you can see, validators are pretty straightforward.
Just add a function that gets called with the view value — the input’s value — and returns whether that value is valid or not.
You can now see the button remains disabled until you enter a valid password.
Add email validation with a loading indicator
We’d like to make sure the supplied email address hasn’t been registered with our site yet as part of the validation.
Starting from version 1.3, Angular has support for asynchronous validators. That means we can easily supply a promise and Angular knows to wait for the promise to either be resolved or rejected to tell whether the field is valid. Sounds like exactly the case where you want to query your server and make sure the field is valid.
Here’s our email validator directive:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
This is pretty similar to the password validator. We’re using $asyncValidators
instead of $validtors
and our function now returns a promise.
While the validation is happening we’d like to show the user that something’s going on. Here’s a very basic change to our template:
1 2 3 |
|
As you can see, while an asynchronous validation is going on, Angular sets the $pending.availableEmail
property on the form’s email attribute (the name email
is coming from name=email
on the input
). Voila!
Adding error messages
Right now our form has all of its functionality implemented. It won’t allow submitting an invalid form.
But, of course we’d like to show the user what he’s done wrong, instead of leaving him to guess.
That’s why Angular introduced the ng-messages
directive. Let’s see how we can add error messages to our email field:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
As you can see, ng-messages
is a lot like ng-switch
and lets us display an appropriate error message depending on the validator that’s failed.
Also note the ng-if=vm.form.email.$touched
bit. We wouldn’t want the form to show these errors right when it first loads. But, when it’s empty it is already invalid.
Angular keeps track of whether the user has interacted with an input, i.e. focused it and then clicked outside of it. That’s what the $touched
means. Awesomely simple!
Check out a live example of the resulting form here, and the source here. In it you can see a bonus: how to use Angular’s forms to style the error messages and the form in invalid states.
“Maintaining AngularJS feels like Cobol 🤷…”
You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete.
Components? Lifecycle hooks? Controllers are dead?
It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines!
Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.
Subscribe and get my free email course with steps for upgrading your AngularJS app to the latest 1.6 safely and without a rewrite.