Introduction to Angular 2 Forms - Template Driven, Model Driven Or In-Between
This post is updated to Angular 2 Release Candidate
In this post we will see how the Angular 2 Forms API works and how it can be used to build complex forms. We will go through the following topics:
- What is Angular 2 Forms all about
- Template Driven Forms, or the Angular 1 way
- Model Driven Forms, or the new Functional Reactive way
- Advantages and disadvantages of both form types
- Learn why its not really about choosing either one or the other
Angular 2 Forms - what is it all about?
A large category of frontend applications are very form-intensive, specially in the case of enterprise development. Many of these applications are basically just huge forms, spanning multiple tabs and dialogs and with non-trivial validation business logic.
Every form-intensive application has to provide answers for the following problems:
- how to keep track of the global form state
- know which parts of the form are valid and which are still invalid
- properly displaying error messages to the user so that he knows what to do to make the form valid
All of these are non-trivial tasks that are similar across applications, and as such could benefit from a framework.
The Angular 2 framework provides us a couple of alternative strategies for handling forms, let's start by going over the option that is the closest to Angular 1.
Angular 2 Template Driven Forms
Angular 1 tackles forms via the famous
ng-model directive (read more about it in this post).
The instantaneous two-way data binding of
ng-model in Angular 1 is really a life-saver as it allows to transparently keep in sync a form with a view model. Forms built with this directive can only be tested in an end to end test because this requires the presence of a DOM, but still this mechanism is very useful and simple to understand.
Angular 2 provides a similar mechanism similarly called
ngModel, that allow us to build what is now called Template-Driven forms. Let's take a look at a form built using it:
There is actually quite a lot going on in this simple example. What we have done here is to declare a simple form with two controls: first name and password, both of which are mandatory fields (marked with the required attribute).
The form will trigger the controller method
onSubmitTemplateBased on submission, but the submit button is only enabled if both required fields are filled in. But that is only a small part of what is going on here.
Angular 2 Forms out of the box functionality
Notice the use of
[(ngModel)], this notation emphasizes that the two form controls are bi-directionally binded with a view model variable, named as simply
More than that, when the user clicks a required field, the field is shown in red until the user types in something. Angular is actually tracking three form field states for us and applying CSS classes for each to the form and its controls:
- touched or untouched
- valid or invalid
- pristine or dirty
These CSS state classes are very useful for styling form error states.
Angular is actually tracking the validity state of the whole form as well, using it to enable / disable the submit button. This functionality is actually common to both template-driven and form-driven forms.
The logic for all this must be in the controller, right?
Let's take a look at the controller associated to this view to see how all this commonly used form logic is implemented:
Not much to see here! We only have a declaration for a view model object
vm, and an event handler used by
All the very useful functionality of tracking form errors and registering validators is taken care without any special configuration!
How does Angular pull this off then?
The way that this works, is that there is a set of implicitly defined form directives (named
FORM_DIRECTIVES) that is being applied to the view. Angular will automatically apply a
NgForm directive to the form element in a transparent way, making it effectively a
If by some reason you don't want this you can always disable this functionality by adding
ngNoForm as a form attribute.
ngControl will also get applied a directive that will register itself with the control group, and validators are registered if elements like
maxlength are applied to the
The presence of
[(ngModel)] will also register a directive that will plug-in the bidirectional binding between form and view model, and in the end there is not much more to do at the level of the controller.
This is why this is called template-driven forms, because all the validation logic is declared in the template. This is nearly identical to the way that this is done in Angular 1.
Advantages and disadvantages of Template Driven Forms
In this simple example we cannot really see it, but keeping the template as the source of all form validation truth is something that can become pretty hairy rather quickly.
As we add more and more validator tags to a field or when we start adding complex cross-field validations the readability of the form decreases, to the point where it will be harder to hand it off to a web designer.
The upside of this way of handling forms is its simplicity, and its probably more than enough to build a very large range of forms.
On the downside the form validation logic cannot be unit tested. The only way to test this logic is to run an end to end test with a browser, for example using a headless browser like
Template Driven Forms from a functional programming point of view
There is nothing wrong with template driven forms, but from a programming technique point of view its a solution that promotes mutability.
Each form has a state that can be updated by many different interactions and its up to the application developer to manage that state and prevent it from getting corrupted. This can get hard to do for very large forms and can introduce a whole category of potential bugs.
Angular 2 provides a different alternative for managing forms, so let's go through it.
Model Driven Forms
A model driven form looks on the surface pretty much like a template driven form. Let's take our previous example and re-write it:
There are a couple of differences here. first there is a
ngFormModel directive applied to the whole form, binding it to a controller variable named
Notice also that the
required validator attribute is not applied to the form controls. This means the validation logic must be somewhere in the controller, where it can be unit tested.
What does the controller look like?
There is a bit more going on in the controller of a Model Driven Form, let's take a look at the controller for the form above:
We can see that the form is really just a
ControlGroup, which keeps track of the global validity state. The controls themselves can either be instantiated individually or defined using a simplified array notation using the form builder.
In the array notation, the first element of the array is the initial value of the control, and the remaining elements are the control's validators. In this case both controls are made mandatory via the
Validators.required built-in validator.
But what happened to
ngModel can still be used with model driven forms. Its just that the form value would be available in two different places: the view model and the
ControlGroup, which could potentially lead to some confusion.
Advantages and disadvantages of Model Driven Forms
You are probably wondering what we gained here. On the surface there is already a big gain:
We can now unit test the form validation logic !
We can do that just by instantiating the class, setting some values in the form controls and perform assertions against the form global valid state and the validity state of each control.
But this is really just the tip of the iceberg. The
Control classes provide an API that allows to build UIs using a completely different programming style known as
Functional Reactive Programming.
Functional Reactive Programming in Angular 2
This deserves it's own blog post, but the main point is that the form controls and the form itself are now Observables. You can think of observables simply as a collection of values over time.
This mean that both the controls and the whole form itself can be viewed as a continuous stream of values, that can be subscribed to and processed using commonly used functional primitives.
For example, its possible to subscribe to the form stream of values using the Observable API like this:
What we are doing here is taking the stream of form values (that changes each time the user types in an input field), and then apply to it some commonly used functional programming operators:
In fact, the form stream provides the whole range of functional operators available in
Array and many more.
In this case we are converting the first name to uppercase using
map and taking only the valid form values using
filter. This creates a new stream of modified valid values to which we subscribe, by providing a callback that defines how the UI should react to a new valid value.
Advantages of building UIs using Functional Reactive Programming (FRP)
We are not obliged to use FRP techniques with Angular 2 Model Driven Forms. Simply using them to make the templates cleaner and allow for component unit testing is already a big plus.
But the use of FRP can really allow us to completely change the way we build UIs. Imagine a UI layer that basically holds no state for the developer to manage, there are really only streams of either browser events, backend replies or form values binding everything together.
This could potentially eliminate a whole category of bugs that come from mutability and corrupted application state. Building everything around the notion of stream might take some getting used it and probably reaps the most benefit in the case of more complex UIs.
Also FRP techniques can help easily implement many use cases that would otherwise be hard to implement such as:
- pre-save the form in the background at each valid state
- typical desktop features like undo/redo
But most importantly, Model-Driven vs Template Driven is not the only option
Model-Driven and Template-Driven are really only two extremes of what we can do with forms. We can mix and match according to out needs, for example:
- we can use
ngModelto read the data, and use
FormBuilderfor the validations. we don't have to subscribe to the form or use RxJs if we don't wish to.
- We can declare a control in the controller, and then reference it in the template to obtain its validity state
Angular 2 provides with several ways to build forms: Template Driven and Form Driven.
The Template Driven approach is very familiar to Angular 1 developers, and is ideal for easy migration of Angular 1 applications into Angular 2.
The Model Driven approach removes validation logic from the template, keeping the templates clean of validation logic. But also allows for a whole different way of building UIs that we can optionally use, very similar to what is available in the React world.
This is not an exclusive choice, we can mix features of both ways of building forms. Its really up to us to assess the pros and cons of each approach, and mix and match depending on the situation choosing the best tool for the job at hand.
If you want to know more about Angular 2 Forms, the podcast of Victor Savkin on Angular Air goes into detail on the two form types and
This blog post gives a high level overview of how Angular 2 will better enable Functional Reactive Programming techniques.
If you are interested in learning about how to build components in Angular 2, check also The fundamentals of Angular 2 components
In case you want to learn Angular 2 from the beginning, have a look at our ongoing YouTube video course for Getting Started With Angular 2: