I've been doing a project with huge forms lately and I've run into performance problems. After some researching, I wondered, what is the better way to select nested FormArrays
from a reactive form into the *ngFor
statement.
For example, I have such a structure of the form, where ...
means N items of FormGroup (inside FormArray) and FormControl (inside FormGroups):
MainFormGroup {
ParentFormArray {
...
FormGroup {
...
NestedFormArray {
...
FormGroup { ... }
...
}
...
}
...
}
}
In Angular 15 I can't render FormArrays this way, as it can be found on older StackOverflow questions:
<div *ngFor="let item of form.controls['ParentFormArray'].controls; let i = index"
Because controls
does not exist inside AbstractControl
. So we need to tell template that ParentFormArray
is an instance of FormArray
directly.
To do that, we have several options (these only the ones I've found):
.ts
that returns FormArray
, but this won't allow us to put a parameter inside (for example, parent FormGroup
of the NestedFormArray
FormArray
and can get a parameter to return nested FormArrays of a specific FormGroup (which is also nested inside parent FormArray)As I know, first two options are bad, because getter and method will be called on every ChangeDetection run. But what about pipes? Is this option more preferable in case of reactive forms?
For example pipe I've written for testing purposes:
@Pipe({ name: "getArray", pure: true })
export class GetArrayPipe {
transform (formGroup: AbstractControl, path?: string): FormArray | null {
return path ? <FormArray>formGroup.get(path) : <FormArray>formGroup;
}
}
And using it like this:
for parent:
<accordion *ngFor="let parentItem of (ParentFormArray | getArray).controls; let i = index"> // parentItem there would be a FormGroup instance
and for nested:
<accordion *ngFor="let nestedItem of (parentItem | getArray:'NestedFormArray').controls; let i = index"> // get controls of the nested FormArray of the parent FormGroup
Or is there a better way to render such multi-level forms?
P.S. tested with console.log
and the result of pipe is great: with methods or getters log
is being called dozens of times per every click (every ChangeDetection with Default strategy), while pipe is being called only 1 time on the initial render.
The best option so far was to use pure pipes. Using this option, angular doesn't need to extract FormArray
s from FormGroup
s every change detection cycle