3 Levels of Nested Form Arrays including Reactive Validations — PART 1

We decided to go Inception on our Angular 5 Forms

View the stackblitz demo here.

We all loved the movie ‘Inception’ and the concept of dream within a dream. Now Nolan did an excellent job at explaining all the intricacies and we hope we can do the same with the nested angular forms we’ve been working on. This is part 1 of a two part blog and we’ll cover the nesting of forms here. Part 2 explains adding reactive validation feature to all the fields and is still being written.

PROBLEM STATEMENT
Build a form to accept 3 hierarchical levels of data with the occurrences of fields in each level unknown. Also, use reactive form validation for validating all the fields. 
This’ll be more clear with examples in the use case.

USE CASE
Say, you had to build a form for a school with multiple classes, each with multiple sections with each sections having multiple students. Emphasis on the fact that we don’t know the number of classes, number of sections or number of students in each section. All the field are added dynamically when data is being entered. Apart from the former example, this can also probably be used in, say, a company where there are multiple departments, each with multiple designations and each designation having multiple employees.

DESIGN and IMPLEMENTATION
You’ll need to refer the stackblitz demo here.

There are two broad angular concepts that are used here -
#1 FormGroup
#2 Reactive Forms

You’ll need familiarity with both to truly understand what has been implemented here.

Note : This is an overview explanation and minute details like the modules that need to be imported aren’t mentioned. Go through the demo carefully and import everything that’s mentioned.

To keep the explanation extremely simple, we’ve decided to use X, Y, Z variables to refer to the level at which the nesting takes place. So, there is X on the outside, Y1 and Y2 inside of X and Z inside of Y (Finding a parallel with the example in the USE CASE section — X can be the Class, Y1 can be the Section Name, Y2 the Section Class Teacher and Z can be the name of the students in the class. Each level can have more than one field and that is why Y1 and Y2 are used).

All other variable are intuitive. Like, we used Xs to name the array which contains X (Incase you’re stuck someplace, comment down below and we’ll get back to you).

The Html

Note: Scroll a little for a figure to help you visualize what’s going on above.

The <form> tag is given [formGroup]=”form”.

FormGroup tracks the value and validity state of a group of AbstractControl instances. The group’s properties include its child controls. The top-level form in the component is a FormGroup.

<! — X Level — ->

The <form> itself has a Form Array (a <form> can have multiple Form Arrays) which is declared here using — formArrayName=”Xs”. One thing to understand and remember is that each Form Array has multiple formGroupName(s). In other words, formGroupName is recursively added after a certain event — here, using the click event at ‘Add X’ button. formGroupName(s) are elements of the array formArrayName=”Xs” and each is referred to by the index acquired with iteration of each *ngFor (line 4). In this cases, it’s ‘ix’. formControlName=”X” is used to link the input field with the TS code in it’s component.

Input of the type button on line 36 calls the function addX( ) onclick event. This is used to add new instance of Xs (with all other levels, Y and Z as well). Refer ‘THE TS’ for further explanation.

<! — Y Level — ->

Inside of the formGroupName=”{{ix}}” exists another formArrayName. It’s called Ys. formArrayName=”Ys” contains formGroupName=”{{iy}}” with two input fields with formControlName Y1 and Y2. Quite like in X Level, formGroup Name=”{{iy}}” is repeated inside of formArrayName=”Ys”.

<! — Z Level — ->

Ys. formArrayName=”Zs” contains multiple occurrences of formGroupName=”{{iz}}”

The TypeScript

ngOnInit( ) is where the form is built on page load. Variable ‘fb’, which is the formbuilder, helps build the form and is declared in the constructor (line 249 of the demo). Quite like the html, Xs is inside of the main formgroup and it contains an array (line 13 of the demo) which is hooked to initX( ) which in turn returns two things. One is the variable X (which is a form field with formControlName X) and Ys (which is another form array). initY( ) returns Y1, Y2 and Zs. Zs is a form array which with yet another form group returned by initZ( ). Lastly, Z is the final form field with formControlName=”Z”.

When ngOnInit( ) executes, the form with single instances of each level is built. Now comes the challenge of adding more fields on the fly.

All the other formgroupnames are added upon click events. The click events accepts 0/1/2 arguments.

The way I look at this is that, the arguments are the locations where the new formgroup name (hence its fields) will be added on click event. When the button in the html line 36 of the demo is clicked, a new occurrence of the entire X level occurs, including all of its children (Y and Z). When the button in the html line 31 of the demo is clicked, based on the argument ‘ix’, a new occurrence of the formgroupname at Y level is pushed at that particular ix. The same happens at Z level as well, just that it accepts 2 arguments which specifies which ix and iy the new formgroupname needs to be pushed to.