Angular comes with three different ways of building forms in our applications. There’s the template-driven approach which allows us to build forms with very little to none application code required, then there’s the model-driven approach using low level APIs, which makes our forms testable without a DOM being required, and last but not least, we can build our forms model-driven but with a higher level API called the
Hearing all these different solutions, it’s kind of natural that there are also probably many different tools to reach the goal. This can be sometimes confusing and with this article we want to clarify a subset of form directives by focussing on template-driven forms in Angular 2.
Let’s start off with a simple login form that asks for some user data:
We’ve probably all built one of those forms several times. A simple HTML form with input controls for a name and an address of a user. Nothing special going on here.
What we can’t see here is that Angular comes with a directive
ngForm that matches the
<form> selector, so in fact, our form element already has an instance of
ngForm is there for a reason. It provides us information about the current state of the form including:
- A JSON representation of the form value
- Validity state of the entire form
Angular comes with a very convenient way of exposing directive instances in a component’s template using the
exportAs property of the directive metadata. For example, if we’d build a directive
draggable, we could expose an instance to the template via the name
draggable like so:
And then, in the template where it’s used, we can simply ask for it using Angular’s local variable mechanism:
From this point on
myDraggable is a reference to an instance of
Draggable and we can use it throughout our entire template as part of other expressions.
You might wonder why that’s interesting. Well, it turns out that
ngForm directive is exposed as
ngForm, which means we can get an instance of our form without writing any application code like this:
Submitting a form and Accessing its value
We can now use
form to access the form’s value and it’s validity state. Let’s log the value of the form when it’s submitted. All we have to do is to add a handler to the form’s
submit event and pass it the form’s value. In fact, there’s a property on the
ngForm instance called
value, so this is what it’d look like:
Even though this would work, it turns out that there’s another output event
ngForm fires when it’s submitted. It’s called
ngSubmit, and it seems to be doing exactly the same as
submit at a first glance. However,
ngSubmit ensures that the form doesn’t submit when the handler code throws (which is the default behaviour of
submit) and causes an actual http post request. Let’s use
ngSubmit instead as this is the best practice:
In addition, we might have a component that looks something like this:
Running this code makes us realize, that the form’s value is an empty object. This seems natural, because there’s nothing in our component’s template yet, that tells the form that the input controls are part of this form. We need a way to register them. This is where
ngControl comes into play.
In order to register form controls on an
ngForm instance, we use the
ngControl simply takes a name as a string and creates a form control abstraction for us behind the scenes. Every form control that is registered with
ngControl will automatically show up in
form.value and can then easily be used for further post processing.
Let’s give our form object some structure and register our form controls:
Great! If we now enter some values and submit the form, we’ll see that our application will log something like this:
Isn’t that cool? We can basically take this JSON object as it is and send it straight to a remote server for what ever we want to do with it. Oh wait, what if we actually want to have some more structure and make our form object look like this?
Do we now have to wire everything together by hand when the form is submitted? Nope! Angular has us covered - introducing
ngControlGroup enables us to semantically group our form controls. In other words, there can’t be a control group without controls. In addition to that, it also tracks validity state of the inner form controls. This comes in very handy if we want to check the validity state of just a sub set of the form.
And if you now think: “Wait, isn’t a form then also just a control group”, then you’re right my friend. A form is also just a control group.
Let’s semantically group our form control values with
As you can see, all we did was wrapping form controls in
<fieldset> elements and applied
ngControlGroup directives to them. There’s no specific reason we used
<fieldset> elements. We could’ve used
<div>s too. The point is, that there has to be an element, where we add
ngControlGroup so it will be registered at our
We can see that it worked out by submitting the form and looking at the output:
Awesome! We now get the wanted object structure out of our form without writing any application code. You might wonder, what role
ngModel plays in the world of forms in Angular 2. Good question!
What about ngModel?
ngModel is the thing in Angular 2 that implements two-way data binding. It’s not the only thing that does that, but it’s in most cases the directive we want to use for simple scenarios. How does
ngModel apply to our template-driven form? Can we still use it? Of course we can!
ngControlGroup provide us a way to structure and access the raw form data,
ngModel can still be used as a domain model to take advantage of two-way data binding. In other words, whereas
form.value is the thing we want to send to a remote server,
ngModel can be the object that feeds our form control, but can be used in other places in our component at the same time.
ngControl provides bindings for
ngModel, so we can simply use it as expected:
More to cover
Of course, this is really just the tip of the ice berg when it comes to building forms. We haven’t talked about validation yet and how to display error messages when the validity state of a form control or a control group changes. We will talk about these and other things in future articles. However, if you’re interested in how to build a custom validator in Angular 2, checkout this article.
Watch out for more articles on forms in Angular 2!
Get weekly updates on the latest and greatest happenings in the Angular world by thoughtram!Tell me more
Angular 2 Master Class in Gent
Learn Angular 2 in our upcoming public training!Join now
Get updates on new articles and trainings.
Join over 700 other developers who get our content first.
How to prevent name collisions in Angular 2 providers
Angular 2's dependency injection improved in many ways. It not only is more flexible when it comes to assembling dependencies...
Exploring Rx Operators: map
In two of our previous articles we already highlighted the importance of Observables in Angular 2. This is the first...
Angular 2 Providers using Map Literals
Defining providers in Angular 2 is pretty easy and straight forward. However, just recently there has been another, shorter syntax...
Custom Validators in Angular 2
Forms are part of almost every web application out there. Angular strives for making working with forms a breeze. Often,...
Angular 2 Change Detection Explained
At NG-NL 2016 I gave a talk about Angular 2's Change Detection and how it makes our apps fast and...
Zones in Angular 2
In our last article we've talked about what Zones are and how they change the way we deal with asynchronous...
You might also be interested in
Custom Validators in Angular 2Read more