Angular Reactive Forms: The Ultimate Guide to FormArray

Netanel Basal
Netanel Basal
Published in
6 min readJan 22, 2020

In this article, I’d like to discuss this peculiar creation — a FormArray, exposed by Angular Reactive Forms. We’ll learn when and how to use it, both in the component and in the template. Moreover, we’ll see how we can apply custom validation to it.

This article assumes that you have a least some working knowledge of Angular FormGroup and FormControl. Let’s get started:

Why Do We Need a FormArray

As you probably know, when we create an AbsractControl such as FormGroup or a FormControl, we can use an array as our value. For example:

That’s nice, but unless you’re dealing with a value accessor that knows how to work with a plain array, it’s probably useless. In most cases, what we want is to employ the Angular API to maintain each item in the array individually; We want Angular to sync the value and to use the validators API.

That’s when a FormArray comes in handy.

What’s a FormArray

A FormArray is responsible for managing a collection of AbstractControl, which can be a FormGroup, a FormControl, or another FormArray.

Just like a FormGroup, which groups AbstractControl objects in an object, a FormArray does the same but in an array. Angular exposes specific APIs to help you manage this collection, which we’ll delve into later on.

Now that we understand what it is, let’s see how we can use it.

Working with a FormArray

Let’s say we need to display a form where users can add, edit, or remove from a list of skills:

We create a FormArray instance and pass an empty array as the initial state value. Let’s see what the skills property contains:

Much like a FormControl and FormGroup, FormArray extends the AbstractControl class. As a result, we can see properties it has in common with them, such as valid, dirty, disabled, etc.

In addition, it has a property named controls, which, as we noted, holds an array that can be populated with instances of AbstractControl.

Now, let’s add a new skill to our collection:

Each time we invoke the addSkill method, we push a new FormControl to the controls array. Let’s use it in the template. First, we need to loop over the FormArray controls property:

Then, we pass each control to the formControl directive so that we can sync each control to a corresponding element:

It’s as simple as that. Let’s see what other built-in methods are at our disposal:

removeAt(index):

This method takes an index and removes the matching AbstractControl. Under the hood, it just calls the native splice method:

insert(index, AbstractControl):

The opposite of the removeAt() method. It inserts a new AbstractControl at the given index in the controls array:

clear():

Removes all the elements from the array:

setControl(index, AbstractControl):

Unlike the insert method, it replaces an existing control with the provided one. In this example it’s used in a replace() method, which replaces the first control with a newly created one:

at(index):

Returns the AbstractControl at the given index in the array. In this example it’s used to return the first control:

Insert a FromGroup

In the previous section, we saw how we manage a collection of FormControls; Now let’s see how we manage FormGroup objects within a FormArray:

Instead of passing a FormControl, we pass a FormGroup. Now let’s use it in the template:

We loop over each control, which in this example is a FormGroup, and pass it to the formGroup directive. Then, it’s just a matter of syncing the FormGroup controls in the way we’re already familiar with — passing the relevant control name to the formControlName directive.

Using a FormArray in a FormGroup

Now that we understand the basics, let’s look at some more practical examples, where we usually employ a FormGroup that contains a FormArray:

Nothing special here, just a regular process of building a FormGroup. Let’s take a look at the template:

We apply the formGroup directive to the form in the template, and inside it, we bind the name control via the formControlName directive. Similarly, We’d like to bind the controls of our FormArray. To do so, we need to follow 3 steps:

First, we need to apply the formArrayName to a container element. Under the hood, it’s the same as running user.get('skills').

Next, like the previous example, we need to loop over the FormArray controls:

Before discussing the final step, let’s define the skills property in our component. The traditional way to do it is by using a getter function that obtains a reference to the FormArray control from the parent FormGroup:

Or we can hold it in a property:

Note that because the returned control is of the type AbstractControl, we need explicitly list the type, in order to get access to the method syntax for the FormArray instance.

Now, for the final step:

The formControlName directive takes the name of the control that we want to sync with to our form element. When we work with a FormGroup we pass the corresponding object key name, for example:

This will search for user.get('name'). But in our case, as we’re dealing with a FormArray — the name is, in fact, the index. If you think about it, it makes sense; In JS, Arrays are a special object whose keys are numeric (based on their index). So eventually, all this code does is something akin to the following:

user.get('skills')[index] => FormControl

Now, let’s see the same process but with a FormArray that contains a collection of FormGroups:

Since the FormArray now holds FormGroup objects, we need to use the formGroupName directive:

user.get('skills')[index] => FormGroup

Then, when we use the formControlName, it’ll search for the closest ControlContainer parent, which in this case is the current parent FormGroup object.

FormArray Validation

As we mention in the beginning, we can apply validation to each one of the AbstractControl as we’d typically do:

In this case, the FormArray validity status is determined based on its AbstractControls validity. So as long as there’s a single invalid AbstractControl, the FormArray valid property will be set to false.

We can also apply a validation at the FormArray level. Let’s say that we need to validate the array size:

As with any AbstractControl, we can pass a validator function, which in our case receives the FormArray instance on each change, and should return null when that array is valid; Otherwise, it returns an object which indicates the nature of the error.

Populating FormArrays with Server Data

Let’s wrap things up with a common example where we need to populate the FormArray controls with data which arrives from the server.

In this case, we can’t use patchValue() or setValue(), as these methods are meant for updating the value of existing controls, and the controls which correspond to the data that’s arrived haven’t been created yet; Instead, we need to create the FormArray from scratch, and set the value for each of its controls:

In case we already have existing controls in our FormArray, we can simply create the controls we need to add, and push them to the array:

🚀 In Case You Missed It

Here are a few of my open source projects:

  • Akita: State Management Tailored-Made for JS Applications
  • Spectator: A Powerful Tool to Simplify Your Angular Tests
  • Transloco: The Internationalization library Angular
  • Forms Manger: The Foundation for Proper Form Management in Angular

Follow me on Medium or Twitter to read more about Angular, Akita and JS!

--

--

Netanel Basal
Netanel Basal

Written by Netanel Basal

A FrontEnd Tech Lead, blogger, and open source maintainer. The founder of ngneat, husband and father.

Responses (21)