In this post, you are going to learn everything that you need to know about the Angular FormArray
construct, available in Angular Reactive Forms.
We are going to learn exactly what is an Angular FormArray
, what is the difference towards a normal FormGroup
, when to use it and why.
We are going to give an example of a common use case that would be hard to implement without a FormArray
: an in-place editable table with multiple form controls per line, where new editable rows can be added or removed on demand by the user.
Table Of Contents
In this post, we will cover the following topics:
- What is an Angular
FormArray
? - What is the difference between a
FormArray
and aFormGroup
? - The
FormArray
API - Creating an in-place editable table using
FormArray
- The
formArrayName
directive - When to use an Angular
FormArray
vs aFormGroup
? - Summary
This post is part of our ongoing series on Angular Forms, you can find all the articles available here.
So without further ado, let's get started learning everything that we need to know about Angular FormArray
!
What is an Angular FormArray?
In Angular Reactive Forms, every form has a form model defined programmatically using the FormControl
and FormGroup
APIs, or alternatively using the more concise FormBuilder
API, which we will be using throughout this guide.
In most cases, all the form fields of a form are well known upfront, and so we can define a static model for a form using a FormGroup
:
As we can see, using a FormGroup
, we can define a group of related form controls, their initial values, and form validation rules, and give property names for each field of the form.
This is possible because this is a static form with a pre-defined number of fields that are all known upfront, which is the most common case when it comes to forms.
But what about other more advanced but still frequently encountered situations where the form is much more dynamic, and where not all form fields are known upfront (if any)?
Imagine a dynamic form where form controls are added or removed to the form by the user, depending on its interaction with the UI.
An example would be a form that is fully dynamically built according to data coming from the backend!
Another more common example of a dynamic form would be an in-place editable table, where the user can add or remove lines containing multiple editable form controls:
In this dynamic form, the user can add or remove new controls to the form by using the Add and Delete buttons.
Each time that the user clicks on the Add button, a new lesson row will be added to the form containing two new form controls.
We will be implementing this example in this guide using FormArray
. But the main question is: why can't this form be implemented using FormGroup
?
What is the difference between a FormArray and a FormGroup?
Unlike our initial example where we defined the form model using FormGroup
via a call to the fb.group()
API, in the case of an in-place editable table, we don't know the number of form controls upfront.
And this is because we can't know upfront the number of rows that the table will have. The user might add an unknown number of rows using the add button, and he might even remove them midway through by using the delete lesson button.
We wouldn't be able to define a form model using FormGroup
, without knowing the exact number of rows. Also, it would be hard to give to the fields pre-defined names.
But we can define a form model for this in-place editable table using a
FormArray
instead.
A FormArray
, just like a FormGroup
, is also a form control container, that aggregates the values and validity state of its child components.
But unlike a FormGroup
, a FormArray
container does not require us to know all the controls up front, as well as their names.
Actually, a FormArray
can have an undetermined number of form controls, starting at zero! The controls can then be dynamically added and removed depending on how the user interacts with the UI.
Each control will then have a numeric position in the form controls array, instead of a unique name.
Form controls can be added or removed from the form model anytime at runtime using the FormArray
API.
The FormArray API
These are the most commonly used methods available in the FormArray
API:
- controls: This is an array containing all the controls that are part of the array
- length: This is the total length of the array
- at(index): Returns the form control at a given array position
- push(control): Adds a new control to the end of the array
- removeAt(index): Removes a control at a given position of the array
- getRawValue(): Gets the values of all form controls, via the
control.value
property of each control
Let's now learn how we can use this API to implement an in-place editable data table.
Creating an in-place editable table using FormArray
Let's first define a reactive form model, using the FormBuilder
API:
As we can see, we did not add any other controls to our form, except for the editable data table itself.
This is just to keep the example simple, but nothing would prevent us from adding any other form properties if we needed to.
In our case, all the controls inside the editable table are going to be inside the FormArray
instance, built using the fb.array()
API.
Initially, the FormArray
instance is empty and contains no form controls, meaning that the editable table is initially empty.
We have added to the component a getter for the lessons
property, in order to make it simple to access the FormArray
instance in a simple and type-safe way.
Also, notice in the editable table screenshot shown earlier, that each row in the table contains two controls: a lesson title field and a lesson level (or difficulty) field.
We would like these fields to be part of the parent form of this component, and affect its validity state. If an error occurs in the title of one of the lessons, then the whole form should be considered invalid.
For this, we are going to create one FormGroup
for each table row, and we are going to add to it two form fields, with their own form control validators.
Dynamically adding controls to a FormArray
In order to add new rows to the table, we are going to need an Add Lesson button in our component template:
When this button gets clicked, we are going to trigger the following code:
As we can see, in order to add a row to the table, we are first creating the form model of a simple form with only the two fields of each row: the lesson title and difficulty.
We then take this lesson row FormGroup
, which is itself also a form control and we add it to the last position of the FormArray
by using the push
API.
Remember, we can add any form control to the FormArray
, and this includes also form groups, which are also controls themselves.
Dynamically removing controls from a FormArray
If you notice in the editable table screenshot shown earlier, every lesson row has a delete icon associated with it, that the user can use to delete the whole lesson row.
Here is what the click handler looks like for this button:
In order to delete a lesson row, all we have to do is to use the removeAt
API to remove the corresponding FormGroup
from the FormArray
, at a given row index.
Notice that in both the add lesson and remove lesson buttons, all we have to do to add or remove a row is to add or remove controls from the form model.
And this is because our UI model is built by looping through the FormArray
elements, and by adding form controls accordingly.
The formArrayName directive
To conclude our exercise, we will now show the full code of the editable table component, and review the template. Let's start with the component code:
And here is what the editable table component template looks like:
Let's now break down what is going on here:
- This editable table is implemented using commonly used Angular Material components
- We are applying the
formGroup
directive to a form container, and linking it to the parent form of the component - This means that the parent form will contain all the child controls of the form, including every control of every row created by the user
- Inside the parent form, we apply the
formArrayName
directive, which links a container element to thelessons
property of the form - This directive is what allows for the
FormArray
instance to track the values and validity state of its child components - We are then using
ngFor
to loop through the form controls of the lessonsFormArray
- every control of the
FormArray
is a form group itself, containing two row controls (title and level) - We then use the
formGroup
directive to define a nested form for every table row, containing two lesson row controls - We then use the
formControlName
directive to bind the lesson fields to the template, like we usually do in any reactive form
As we can see, our editable table form is simply a form with a list of nested child forms, one form per table row. And each table row form contains two controls inside it.
And with this, our in-place editable table is fully implemented! The user can freely add and remove lesson rows to the form.
The parent form will only be considered valid once every row added by the user is filled in with valid values.
Summary
Let's quickly summarize everything that we have learned about FormArray
, and do one final comparison to FormGroup
.
As we can see, the FormArray
construct is very powerful and comes in especially handy in situations where we want to build our form model in a more dynamic way.
When to use an Angular FormArray vs a FormGroup?
When building the model of an Angular form, most of the time we want to use a FormGroup
and not a FormArray
, so that should be the default choice.
As we can see, the FormArray
container is ideal for those rarer situations when we don't know the number of form controls upfront or their names.
The FormArray
container is ideal for more dynamic situations where the content of the form is in general being defined at runtime, depending on user interaction or even backend data.
In our example, we have used FormArray
to implement an in-place editable table, because we believe that is its most common use case.
In the table example, the controls in the FormArray
were FormGroup
instances, containing form controls themselves, but notice that this is not mandatory.
A FormArray
can contain any type of controls inside it, and this includes plain form controls, FormGroup
instances and even other FormArray
instances.
With FormArray
, you can implement in Angular all sorts of advanced dynamic form scenarios, but keep in mind that unless you really need the power of
FormArray
, FormGroup
will be the correct choice for most cases.
I hope that you have enjoyed this post, if you would like to learn a lot more about Angular Forms, we recommend checking the Angular Forms In Depth course, where all sorts of advanced forms topics (including FormArray
) are covered in much more detail.
Also, if you have some questions or comments please let me know in the comments below and I will get back to you.
To get notified of upcoming posts on Angular, I invite you to subscribe to our newsletter:
And if you are just getting started learning Angular, have a look at the Angular for Beginners Course: