Angular2: Building nested, reactive forms
In Angular2, there are two approaches to building forms. One are template-driven forms. The other way are reactive forms. When we build rather sophisticated forms, we might want to write custom components for custom form elements and then compose several of those components into a concrete form.
In this article, we will show by example how to implement nested forms with the reactive forms API. In the official Angular2 documentation, this approach is also called “dynamic forms”. We will also try to understand the concept of a form model that backs concrete form and input elements.
To make things clear to the reader it’s a good idea to highlight the differences between template-driven forms and reactive forms first. In the second part, we will build a nested, reactive form.
Understanding the form model
In Angular2, an application holds a form model that represents input values, validation state, its set of controls, and so on. The basic building blocks of that API are FormControl
, FormGroup
and FormArray
. Instances of these objects are then mapped to the DOM.
Let’s just take a simple form with the following controls:
- name: text, required
- quantity: number
A template-driven form
Template-driven forms are probably the most natural to anyone working with Angular2 forms for the first time. It starts with a HTML template, a <form>
element and two <input>
elements.
Two directives bring this form into life: ngForm
and ngModel
.
First, the expression #myForm="ngForm"
creates an instance of thengForm
directive and assigns it to the template variable myForm
.
Notice that we use the variable in following parts of the template. It’s a so-called template reference variable.
NgForm
builds a form model by reading the HTML template.
The form is reflected by the expressionmyForm.form
, an instance of FormGroup
.
Then, for each element with a NgModel
directive, Angular creates a FormControl
.
If a required attribute is set on the input element, it adds the required validator to the control. The FormControl
instance is then added to the parent FormGroup
, where it can be accessed by its name. Thus, we get the following model:
myForm.form
: aFormGroup
representing the formmyForm.form.name
aFormControl
representing the first inputmyForm.form.quantity
: aFormControl
representing the second input
Broadly speaking, with template-driven forms we declare our form model by writing plain-old-fashioned HTML forms and let Angular create a form model out of the DOM.
A reactive form
Now, in reactive forms we write that form model in code and then bind instances of FormGroup
and FormControl
into the DOM.
In this template, we bind the member myForm
to the DOM element form by writing [formGroup]="myForm"
.
The expression instantiates a FormGroupDirective
which handles the binding.
Then, we bind FormControl
instances to input elements in the DOM. An example is: formControlName="quantity"
.
Guess what? That expression creates a FormControlDirective
behind the scenes.
In the component’s code, the form model is created in the ngOnInit()
method. We also need to instantiate and add Validators
in code.
A so-called FormBuilder
lets you create those instances. A very different style of writing is to create instances with the new
keyword. Both styles have the exact same effect — in times of dependency injection just no one is used to writing new
anymore, which is probably the reason for a builder, that, in turn, gets injected as a constructor dependency.
You can try out both forms here:
Should I stay or should I go?
What’s all the fuzz about template-driven forms and reactive forms? Should I use the former? Or the latter?
Obviously, the style of code-writing is different. In template-driven forms, we, the developers, start writing a template and let Angular create a form model. In reactive forms, we start at the other end by writing a form model and bind to that model in templates.
When we’re building nested forms, that’s an important point. With reactive forms, we can build a form model and pass that model (or parts of it) around from one component to another through Input properties. We are going to do that in the second part of this story.
Building a nested, reactive form
We are building upon or previous example and enhance the form to make it more interactive.
The user can add and remove items from the form and we will require the user to enter a total quantity sum of at least 300 items.
Notice that we put a red border around the invalid parts of the form.
So, even if you enter a name for the second item, the items of the FormArray
will be invalid and marked in red since the sum of quantities is below the threshold (100+100 < 300).
The form will not be valid before you enter 200 and 100 in the quantity inputs (or anything else that sums up to 300 or more).
Show me the code!
Here’s the full code of that component and a plunker below. You can quickly read through that or even skip over it. We will go and refactor that into several components and turn to the details then.
Extracting an ItemFormControlComponent
First idea is obviously to extract a component that represents a single item in the form array. Let’s call that an item form control and its corresponding component is an ItemFormControlComponent
.
To make that work, we pass its index and a FormGroup
instance as input properties. The FormGroup
is created by a simple static buildItem()
function.
We also need to keep the remove button alive, which we do by adding an output property. When a form item is removed, it will emit the index of the item. In the parent component, we wire all the things up by adding our custom <item-control>
tag to the template.
Extracting an ItemsFormArrayComponent
The second step is to extract the array of items to a component of its own.
Let’s call that the items form array which results in a component name of ItemsFormArrayComponent
.
Again, we pass a FormArray
as an input property from the parent to the newly created component. Notice the slight difference to the previous approach: now, we write formArrayName="items"
on the component’s host element. The code is:
<items-array formArrayName="items" [items]="myForm.get('items')">
Of course, we can re-use ItemFormControlComponent
from the previous step. Here’s what that looks like:
A tree of components and directives
Let’s try to visualize how the tree of components looks like in our application.
Components are highlighted in tomato-red boxes. Directives are shown in white boxes.
The second line of text illustrates the DOM element which is the mount target of a particular component or directive.
Input properties are written on the edges that connects components/directives.
As you can see, instances of a form model are passed from the top-most component further down the tree.
Going on from here
Let’s just make a quick recap and summary of the things that we have looked at:
The form model API is built around FormControl
, FormArray
,FormGroup
. Directives connect an existing form model into templates (reactive forms), or build a form model from a template (template-driven forms).
A form model can be passed from one component to another using input properties. This technique allows building rather sophisticated and complex forms who are composed of several components. Reactive forms are a good fit for that idea, making it relatively easy to build nested forms.
If you want to check out the complete demo application, take a look the Plunker below. Feedback is also very welcome!