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 of ng-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 or ng-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 the ng-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:

If you enjoyed this post, these are some other popular posts on our blog: