In this post, we will learn how the Angular Forms API works and how it can be used to build complex forms. We will go through the following topics:
- Template Driven Forms (similar to AngularJs ng-model)
- The ngModel, ngForm and ngSubmit directives
- Understanding the Form state CSS classes: touched, dirty, valid
- Reactive Forms compared to Template Driven forms
- The FormBuilder API
- The Reactive Forms Observable-based API
- Updating Form Values, How To Reset a Form
- Advantages and disadvantages of both form types
- Can and should the two form types be used together?
- Which form type to use and why?
- Summary
This post is part of the ongoing Angular Forms series. Here are two other related posts that you might find interesting:
Angular Forms - what is it all about?
A large category of frontend applications are very form-intensive, especially 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 the users know what to do to fix the wrong form values
All of these are non-trivial tasks that are similar across applications, and as such could benefit from a framework.
The Angular framework provides us a couple of alternative strategies for handling forms: Let's start with the option that is the closest to AngularJs.
Note: AngularJs is a completely different framework than Angular, its his non-backwards compatible predecessor
Angular Template Driven Forms
AngularJs tackled forms via the famous ng-model
directive (read more about it in this post).
The instantaneous two-way data binding of ng-model
in AngularJs was really a life-saver as it allowed to transparently keep in sync a form with a view model.
Forms built with this directive could only be tested in an end to end test because this requires the presence of a DOM, but still, this mechanism was very useful and simple to understand.
Angular now provides an identical mechanism named also ngModel
, that allow us to build what is now called Template-Driven forms.
Note that Angular
ngModel
includes all of the functionality of its AngularJs counterpart.
Enabling Template Driven Forms
Unlike the case of AngularJs, ngModel
and other form-related directives are not available by default, we need to explicitly import them in our application module:
We can see here that we have enabled Template Driven Forms by adding FormsModule
to our application root module.
Note that simply by including this FormsModule
in your application, Angular will now already apply a NgForm
directive to every <form>
HTML template element implicitly, unless you annotate the form element with the ngNoForm
attribute (more on this later).
With this initial configuration in place, let's now build our first Angular Form.
Our First Template Driven Form
Let's take a look at this form built using the template driven way:
There is actually quite a lot going on in this simple example. What we have done here is declare a simple form with two controls called first name and password, both of which are mandatory fields (as they are marked with the required
attribute).
How does ngForm work?
Notice the nyForm
template export. We are using to get a reference to the ngForm
directive, which is implicitly applied to all HTML <form>
elements by the Angular Forms module.
This directive is responsible for tracking the overall value of the the form , which contains the values of all of its form fields.
The ngForm
directive will also keep track of the overall validity state of the form, which is dependent on the validity state of its form fields.
But how does this directive know about the individual controls of the form?
How does ngModel work?
Notice that each form control has the ngModel
directive applied to it. This directive will bind to the corresponding HTML element, in this case the two input fields first name and password.
The ngModel
directive will keep track of the value typed in by the user with each key pressed, and it will also keep track of the validity state of that particular form control only.
The ngForm
parent directive will then interact with all its child ngModel
directives, and build a model of the whole form, with all its field values and validity states.
How does ngSubmit work?
The form will trigger the component method onSubmitTemplateBased
on submission, but the submit button is only enabled if both required fields are filled in.
The component class where onSubmitTemplateBased()
is defined will then get access to the latest data via the user
member variable.
Notice that the submission of this form will not trigger a backend HTTP POST request, like in the case of a plain HTTP form submit.
The ngSubmit
directive will ensure that this submission does not occur, and instead that the onSubmitTemplateBased()
method gets called.
The ngSubmit
directive allows us to access the native form submission event if we need to, via $event
. But other than that, it works just like if we would have made the submit button a plain button (without type=submit
) and added it a click handler instead.
But all of this is only a small part of what is going on here.
NgModel Validation Functionality
Notice the use of [(ngModel)]
, this notation emphasizes that the two form controls are bi-directionally bound with a view model variable, named as simply user
.
This
[(ngModel)]
syntax is known as the 'Box of Bananas' syntax :-) This is a useful menemonic to remember what type of parantheses (square or round) should be typed first
More than that, when the user clicks in a required field, the field is shown in red until the user types in something.
Angular is actually tracking three separate form field states for us and applying the following CSS classes to both the form and its controls:
- ng-touched or ng-untouched
- ng-valid or ng-invalid
- ng-pristine or ng-dirty
All of these CSS class pairs are mutually exclusive, and they are very useful for styling form error states, both at the individual form control level but also at the level of the whole form.
Understanding the Angular Forms CSS state classes
Here is the meaning of these three CSS state class pairs:
- All form controls and the form itself start in state
ng-untouched
, meaning that the user has not yet tried to interact with the control (or form) - once the user attempts to interact with a form control at least once, by clicking on it and maybe even clicking away without entering any value, the control will be considered touched and the
ng-touched
CSS class with be applied to it, instead ofng-untouched
. - If at least one form control inside a form is touched, then the whole form will be considered touched as well, and get applied the
ng-touched
CSS class - Each form control also has a validity state, meaning that its current value is either valid or invalid. According to that, the CSS classes
ng-valid
orng-invalid
will be applied correspondingly. - If at least one form control inside a form is invalid, then the whole form is also considered invalid, and the CSS class
ng-invalid
gets applied to the form as well - This means that in order for a form to be considered valid, then all of its controls need to have valid values filled in
- The form often gets initialized with data from the the backend, in the case of Edit forms, as opposed to Creation forms
- The forms controls start in a pristine state, meaning that the data has not yet been modified by the user. The control will then get applied the CSS class
ng-pristine
- Once the user modifies the form data, we have new data that is not yet saved to the backend. We then say that the form control is dirty, and the
ng-dirty
CSS class gets applied by Angular, and theng-pristine
class gets removed - The notions of touched and dirty are closely related but separate: dirty means that the data is different than the original form data, and touched means that the user alredy tried to interact with the form control. But the control being touched by the user does not mean that the data was modified already, and so we have two separate sets of CSS state classes
In the form example above, Angular is tracking the validity state of the whole form, using it to enable/disable the submit button.
Much of this functionality (including the CSS state classes) is actually common to both template-driven and reactive forms.
The logic for all this must be in the component class, right?
Let's take a look at the component associated with this view to see how all this form logic is implemented:
Not much to see here! We only have a declaration for a view model object user
, and an event handler used by ngSubmit
.
All the very useful functionality of tracking form errors and registering validators is taken care for us 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 that are being applied to the view. Angular will automatically apply a form-level ngForm
directive to the form in a transparent way, creating a form model.
If by some reason you don't want this you can always disable this functionality by adding ngNoForm
as a form attribute.
Furthermore, each input will also get applied a ngModel
directive that will register itself with the parent ngForm
, and validators are registered if elements like required
or maxlength
are applied to the input.
The presence of [(ngModel)]
will also create a bidirectional binding between the form and the user model.
This is why this type of forms are called template-driven forms, because both validation and binding are all setup in a declarative way at the level of the template, without any code needed at the level of the component class.
Is ngModel just for bi-directional data binding?
We have shown our first template driven form above using the bi-directional data binding [(ngModel)]
way of tracking values, because we believe that this is the most common use case for template driven forms as its also very similar to that way that it was done with ng-model
in AngularJs.
But bi-directional data binding is not the only way to use ngModel
.
Sometimes we just want to create a form and initialize it, but not necessarily do bi-directional binding.
Using ngModel for one-way data-binding only
We could instead want to let the user edit the form initial values and press submit, and only then get the latest value edited by the user.
We can do so by using the one-way binding [ngModel]
syntax:
This will allow us to initialize the form by filling in the fields of the user member variable:
Notice that now, when the user types in new values in the form, these values will no longer be immediately reflected in the user component member variable, like before when we were using bi-directional data binding.
This means that now, when the user submits the form we need to get the latest form value from the ngForm
directive, by using the myForm
export, and pass it on to onSubmitTemplateBased()
.
What if we only need form validation, without any type of binding?
So far we have been using ngModel
to do either one-way or bi-directional data binding between the form controls and the component class.
But for example, creation forms don't need initial values, so in those cases we don't need any kind of binding, no even to initialize the form.
If we want to get only the validation and value tracking functionality of ngModel
without any type of binding, we can do so with the following syntax:
As we can see, ngModel
is simply a plain Angular directive that binds to each form control and tracks its value and validity state.
Bi-directional data binding is only one the several use cases of ngModel
, but its not the only way to use it.
Advantages and Disadvantages of Template Driven Forms
In the simple template-driven example above we cannot really see it, but keeping the template as the source of all the form validation rules is something that can become pretty hard to read and maintain very quickly.
As we add more and more validator tags to a field or when we start adding complex cross-field validations the readability and maintainability of the form decreases.
It might become harder to hand over the form to a web designer for example, as the template gets more complex and full of business validation rules.
The upside of this way of handling forms is its initial simplicity, and it's probably enough to build small to medium-sized forms.
It's also very similar to what was done in AngularJs with ng-model
, so this programming model will be familiar to a lot of developers already.
On the downside, the form validation logic cannot be easilly unit tested and the templates can become complex rather quickly.
There is an alternative way in Angular for building forms, which is the
ReactiveForms
module. We will present it now, and in the end compare both.
Angular Reactive Forms
A reactive form looks on the surface pretty much like a template driven form. But in order to be able to create this type of forms, we need to first import a different module into our application:
Note that here we imported ReactiveFormsModule
instead of FormsModule
. This will load the reactive forms directives instead of the template driven directives.
If we find ourselves in a situation where we would happen to need both, then we should import both modules at the same time.
Our First Reactive Form
Let's take our previous form example and re-write it but this time around in reactive style:
There are a couple of differences here. First, there is a formGroup
directive applied to the whole form, binding it to a component variable named form
.
Notice also that the required
validator attribute is not applied to the form controls. This means the validation logic must be somewhere in the component class, where it can be more easilly unit tested.
What does the component class look like?
There is a bit more going on in the component class of a Reactive Form, let's take a look at the component for the form above:
We can see that the form is really just a FormGroup
, which keeps track of the global form value and the validity state.
The controls themselves can be instantiated individually using the FormControl
constructor. The end result is a programmatic definition of our form model with all of its controls and validity rules, that is created programmatically at the level of the component class, and not the template.
The FormBuilder API
The way that we have just shown of creating form models by explicitly calling the FormGroup
and FormControl
constructors can become a bit verbose, especially for larger forms.
In order to alleviate this problem, we can also use the following equivalent notation, created with the built-in FormBuilder
service:
As we can see, instead of calling the FormGroup
and FormControl
constructors directly, we have instead used a simplified array notation for defining the form model, which is a bit more concise.
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.
This reactive version of the form is fully equivalent to the previous template driven version: it provides the exact same functionality.
Advantages of Reactive Forms vs Template Driven Forms
You are probably wondering what we gained here. On the surface there is already a big gain: the template of the component is a lot cleaner, and focuses only on presentation logic.
Having a lot of directives in the template for defining business validation rules can easily become messy for larger forms, so its much cleaner to define that logic on the component class instead.
All the business validation rules for each of the form fields has been moved to the component class, where they can be unit tested a lot more easily.
Moving the form model definition to the component makes it very easy to define the form dynamically if necessary, based for example on backend data, so its easier to implement more advanced use cases.
Also, with reactive forms, its a lot easier to create a custom validator: we just have to define a function and plug into our configuration.
While with template driven forms, we have to write also a custom directive which is a bit more complicated then simply writing a function, in order to get the exact same functionality.
So as we can see, the reactive forms module allows to define the form model programmatically instead of declaratively via the view, and they do provide some advantages when compared to template driven forms.
But why are they called reactive forms?
The Reactive Forms Observable-based API
These types of forms are called Reactive Forms because the individual form controls and also the form itself provide an Observable-based API.
This means 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 RxJs operators.
For example, it's possible to subscribe to the Form stream of values using the valueChanges
Observable:
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 RxJs operators: map
and filter
.
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 valid-only values to which we can subscribe, by providing a callback that defines how the UI should react to a new valid value.
This observable-based API makes it easy to implement many advanced use cases that would otherwise be rather hard to implement such as:
- pre-save the form in the background as a draft, as the user progressively fills in more fields
- typical desktop features like undo/redo
Updating Form Values
We have APIs available for programmtically updating the whole form, or just a couple of fields. For example, let's create a couple of new buttons on the reactive form above:
We can see here that there are two buttons for updating the form value, one for the partial updates and the other for full updates. This is how the corresponding component methods look like:
We can see that FormGroup
provides two API methods for updating form values:
- we have
patchValue()
which partially updates the form. This method does not need to receive values for all fields of the form, so we can use to update only a few fields at a time - there is also
setValue()
, to which we are passing all the values of the form. In the case of this method, values for all form fields will need to be provided, otherwise, we will get an error message saying that some fields are missing
We might think that we could use these same APIs to reset the form by passing blank values to all fields.
That would not work as intended because the pristine and untouched statuses of the form and its fields would not get reset accordingly.
How To Reset a Form
Using the FormGroup
API, we can easilly reset everything back to pristine and untouched:
Let's now see if its possible to mix both type of forms, and talk about if that is advisable.
Reactive vs Template Driven: can they be mixed?
Reactive and Template-Driven under the hood are implemented in the same way: there is a FormGroup
for the whole form, and one FormControl
instance per each individual control.
The difference is that, with Reactive Forms we are defining the form model programmatically in an explicit way in our component class, and we then link the form model to the template using directives such as
formGroup
or formControlName
.
This is as opposed to template driven forms, where the same form model made of a FormGroup
and FormControl
instances is built behind the scenes for us by a series of directives applied to the template, like ngForm
and ngModel
.
If by some reason we would need to, we could mix and match the two ways of building forms.
But in general, it's better to choose one of the two ways of doing forms, and using it consistently throughout the application.
Reactive Forms or Template Driven Forms: which one to choose, and why?
Reactive Forms scale better for larger and more complex forms, and support better more advanced use cases.
Reactive Forms also promote a clearer separation between business logic and presentation logic, which leads to clearer, easier to read and more maintainable HTML templates.
With Reactive Forms, its much easier to implement custom validation rules, like for example a password strength validator or a multi-field validation rule.
For doing that, we just need to write a function, while in template driven forms we will have to implement an additional validation directive to call the function and make the bridge to the template.
In priciple, everything can be done using both form types, but there are a lot of use cases both common and advanced that are just simpler to implement using reactive forms.
Which form type to choose?
Are you migrating an AngularJs application into Angular? That is the ideal scenario for using Template Driven Forms, as the ngModel
supports bidirectional data binding, just like the AngularJs ng-model
directive.
But other than that, Reactive Forms are a much better choice. They are more powerful, easier to use and encourage a better separation between view and business logic.
For these reasons, Reactive Forms tend to work better than Template Driven forms, and they are the better default choice for new applications.
As mentioned before, we want to avoid situations where we are using both form types together, as it can get rather confusing.
But it's still possible to use both forms together if by some reason we really need to.
Let's now quickly summarize everything that we have learned about template driven and reactive forms, and talk about when to use each and why.
Summary
Here are the differences between Template-Driven and Reactive Forms:
-
Template Driven Forms need the FormsModule, while Reactive forms need the ReactiveFormsModule
-
Template Driven Forms are based only on template directives, while Reactive forms are defined programmatically at the level of the component class
-
Reactive Forms are a better default choice for new applications, as they are more powerful and easier to use.
-
The Template Driven approach is very familiar to AngularJs developers and is ideal for easy migration of AngularJs applications into Angular.
-
The Reactive approach removes validation logic from the template, keeping the templates cleaner.
-
Reactive forms are easier to use in general and support better more advanced use cases via its Observable-based API.
-
It's not an exclusive choice but for a matter of consistency, it's better to choose one of the two approaches and use it everywhere in our application, preferably Reactive forms
I hope that you enjoyed this post, if you have any questions please let me know in the comments section below and I will get back to you.
In case that you want to learn Angular Forms in detail (both reactive and template-driven), you can check the Angular Forms In Depth course.
If you are just getting started learning Angular, have a look at the Angular for Beginners Course:
Other posts on Angular
If you enjoyed this post, these are some other popular posts on our blog:
- Angular Router - How To Build a Navigation Menu with Bootstrap 4 and Nested Routes
- Angular Router - Extended Guided Tour, Avoid Common Pitfalls
- Angular Components - The Fundamentals
- How to run Angular in Production Today
- How to build Angular apps using Observable Data Services - Pitfalls to avoid
- Angular ngFor - Learn all Features including trackBy, why is it not only for Arrays ?
- Angular Universal In Practice - How to build SEO Friendly Single Page Apps with Angular
- How does Angular Change Detection Really Work?