Building recursively nested @angular reactive forms: a clean approach
Tags: angular, reactiveforms, nestedforms, controlvalueaccessor
Published: 2020-06-13 20:50:59

# The use case

<small>*Side Note: this post was inspired by this [StackOverflow question](https://stackoverflow.com/questions/62352286/angular-deeply-nested-reactive-form-cannot-find-control-with-path-on-nested-for/62355590?noredirect=1#comment110349898_62355590)*</small>

Let's talk about the below behavior:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/aig9g56z7oslaeablirz.gif)

If you look carefully at the above animated gif you'll notice 2 important things about the displayed form behavior:

  1. It's dynamically created
  2. It can be recursively nested indefinitely

A static image will help us to better understand the scenario:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/hohsn9c259uqsazqsgei.png)

I've highlighted some repeating parts on the form above. The red parts are very interesting, so take your time looking at them. Have you notice something with them? It's recursively nested. You can see that Group 2 has a Nested Group 1 inside it and both of them are structurally identical. In fact, Group 2 and the Nested Group 1 have the same elements: conditions and the same action buttons bar.

How would we build up a dynamically, recursively nested form like the one shown in the image above? What if we want to create and fill-up the form based on some data you load from a server? Keep reading and we'll get there in a minute.

The complete code discussed here is available at [Stackblitz.com](https://stackblitz.com/edit/httpsdevtojulianobrasilwriting-nested-recursive-angular-reactiv?file=src%2Fapp%2Fgroup-control%2Fgroup-control.component.ts):
{% stackblitz httpsstackoverflowcoma623555906433166 view=preview %}

# The pseudo component tree

We'll build the following structure (in this "pseudo component tree" each label started with `app` represents an angular component):

```html
app-group-control
    |
   app-action-buttons-bar
    |
   conditions:
     app-condition-form
     app-condition-form
     app-condition-form
     ...
    |
   groups:
     app-group-control
     app-group-control
     app-group-control
     ...
```
In the above pseudo component tree, `app-group-control` is an angular reactive form that contains an action bar and 2 `FormArray` controls: `conditions` and `groups`.

Now I want you to be a curious developer and notice that `groups` is a collection of `app-group-control` components. Imagine this: you're gonna a build a component: `app-group-control`. In its template, among other things, it has a collection of... itself. It's a *composite component*.

Let's dive a little in each part of this component.

## Action bar

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/lpiwnoygy6kvo3zbt76m.png)

Inside `app-group-control` the first thing we'll find is this action bar (`ActionButtonsBarComponent` in [Stackblitz demo](https://stackblitz.com/edit/httpsstackoverflowcoma623555906433166?file=src%2Fapp%2Faction-buttons-bar%2Faction-buttons-bar.component.ts)) that allows us to:
  - Add a `app-group-control` to `groups` `FormArray` (let's call it a "nested group")
  - Add a condition to the `conditions` `FormArray`
  - When the user clicks on the delete group button, it will ask the component that hosts this instance of `app-group-control` to be removed

This is a very simple component with 3 `@Ouput()`-decorated properties:

```javascript
  @Output()
  remove = new EventEmitter<void>();
  @Output()
  addGroup = new EventEmitter<void>();
  @Output()
  addCondition = new EventEmitter<void>();
```
## Condition

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/lacajenhv9784advcidj.png)

The condition component (`ConditionFormComponent` in [Stackblitz demo](https://stackblitz.com/edit/httpsdevtojulianobrasilwriting-nested-recursive-angular-reactiv?file=src%2Fapp%2Fcondition-form%2Fcondition-form.component.ts)) is also a very simple component. But, it has an important thing you should notice: it implements the `ControlValueAccessor` interface.

The `ControlValueAccessor` interface is what makes it possible for you to turn your components into angular `FormControl`s. And like any `FormControl`, they can be part of forms (for example, they can receive the `formControlName` directive and be controlled by the available `@angular/forms` utilities and methods).

The `ControlValueAccessor` is also, what make it possible for us to nest forms in the most possible simple way. I've seen a lot of people doing crazy things to nest forms, like passing the parent `FormGroup` to a child component in order to reuse it in the child component's form. Maybe it's a matter of style, but I'd strongly recommend anyone to just turn that child component (that contains the nested form) into a `FormControl`. By doing that you'll be flattening the form's structure. I'd suggest two Kara Erickson videos if you want to learn more cool stuff about angular forms. Finish reading this post and, after that, scroll your page back to check out these amazing videos.

{% youtube xYv9lsrV0s4 %}

{% youtube CD_t3m2WMM8 %}

## The wrapper form component

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/kdr6yjflmgeat2f7n5h2.png)

Finally we have this wrapper component, that contains the form that wraps everything (`GroupControlComponent` in [Stackblitz demo](https://stackblitz.com/edit/httpsdevtojulianobrasilwriting-nested-recursive-angular-reactiv?file=src%2Fapp%2Fgroup-control%2Fgroup-control.component.ts)).

Basically, its template is:

```html
<form [formGroup]="_form">
  <app-action-buttons-bar (remove)="remove.emit()"
                          (addGroup)="_addGroup()"
                          (addCondition)="_addCondition()">
  </app-action-buttons-bar>
  <ng-container formArrayName="conditions">
    <app-condition-form *ngFor="let c of _conditionsFormArray?.controls; index as j" 
                        (remove)="_deleteCondition(j)"
                        [formControlName]="j" 
                        [formLabel]="'Condition ' + (j+1)">
    </app-condition-form>
  </ng-container>
  <ng-container formArrayName="groups">
    <app-group-control *ngFor="let s of _groupsFormArray?.controls; index as i"
                        (remove)="_deleteGroupFromArray(i)" 
                        [formControlName]="i"
                        [formLabel]="'Nested Group '+ (i + 1) + ':'">
    </app-group-control>
  </ng-container>
</form>
```
If you check the code in Stackblitz, you'll notice that `<app-condition-form>` has its own form inside. `<app-group-control>` also has its own form (we're actually looking at it in the code above). Thanks to `ControlValueAccessor`, nesting these two forms inside a parent form was a piece of cake. Take a look again... we're not only nesting two forms in a completely transparent way, which would be a huge achievement per se. Considering that `conditions` and `groups` are `FormArrays`, we're, in fact, nesting an indefinite number of forms inside the parent form, without the need of any hack, using a simple, clean and robust approach. Pretty cool, don't you think?

# The recursion

Like if it wasn't awesome enough to nest an indefinite number of forms in the first nested level, we're **nesting host component instances inside itself... for an indefinite number of nesting levels down**. It deserves a huge OMG!!! I hope this can give you a measure of the power of angular component-based structure.

# Create the form based on incoming data

As a part of the implementation of the `ControlValueAccessor` interface, we implemented the `writeValue(...)`. It is called by angular whenever the `FormControl.setValue()` or `FormControl.patchValue()` is executed by the host form. And this is a perfect place to analyze the incoming data and create/remove/fill-up controls of our form according to the incoming data.

If you take a look at this [modified stackblitz demo](https://stackblitz.com/edit/httpsdevtojulianobrasilwriting-nested-recursive-angular-reacti2?file=src/app/group-control/group-control.component.ts), you'll see how we can achieve this with two tiny snippets (both of them in the `writeValue(...)` functions of the `GroupControlComponent`):

```js
writeValue(value: Record<string,any>) {
  ...
  // Here we create the `conditions` controls based on 
  //   existent `conditions` attribute in the value
  if (value && value.conditions && value.conditions.length) {
    this._conditionsFormArray.clear();
    value.conditions.forEach(c => this._addCondition());
  }
  ...
  // Here we create the `groups` controls based on 
  //   existent `groups` attribute in the value
  if (value && value.groups && value.groups.length) {
    this._groupsFormArray.clear();
    value.groups.forEach(g => this._addGroup());
  }
  ...
}
```

So, in the `AppComponent` (in the demo), when we call `this._form.patchValue(data);` in the `buildFormFromData()` method, our form is built automatically to accommodate `data` in the right fields. With `ControlValueAccessor` in place, this was pretty easy, wasn't it?

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/946dpgfdhuswwpi2si11.gif)

# Final Considerations

If you try to build this same form without angular `ControlValueAccessor`, I can guarantee you painful headaches during all the process. For some reason, the `ControlValueAcessor` is one of the most underestimated angular features among developers. Not using it makes the overall process of building complex forms an overwhelming task.

## Give my schematics library a try

BTW, I've built an schematics libray that generates the skeleton of a component with the `ControlValueAccessor` implementation for you. If you want to, fell free to try it in your projects (it's a `devDependency`, so it won't add any nano dependency to your project): https://www.npmjs.com/package/@julianobrasil/schematics-components

Credits: Cover image from [https://undraw.co](https://undraw.co)