Is it posible to have a formArray nested inside of other formArray and show it properly in the HTML view?
I'm building a paginated form based on an API response, so I'm iterating over my response to create this form, so I would end up having something like:
this.form = this.formBuilder.group({ pages : this.formBuilder.array([ this.formBuilder.array([]) ]) });
So my form looks like:
-> this.form = FormGroup (controls)
-> pages = FormArray (controls)
-> 0 = FormArray (controls)
-> 0 = FormGroup (controls)
-> Name (my form fields)
-> Active (my form fields)
So iterate through it in the DOM is quite weird to me and I was wondering if there's a better way to achieve this or if this is the proper way indeed
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<ng-container formArrayName="pages">
<ng-container *ngFor="let page of pages.controls; let i = index">
<div *ngIf="i === paginationIndex" [formGroupName]="i">
<div *ngFor="let myControl of page['controls']; let i = index" [formGroupName]="i">
{{myControl.get('name')?.value }}
<input type="text" formControlName="active">
</div>
</div>
</ng-container>
</ng-container>
<div>
<button class="btn" type="submit">Submit</button>
</div>
</form>
Yes, we can. We should create two formArrayName
one for pages
and another with the nested form array name index. Apart from this, I used some functions to set the type of the elements in HTML, which were not inferred, please refer the below working example.
I don't think it's a good idea to use formArrayName
, formGroupName
etc. on ng-container
elements. Instead go for a plain DIV, because the elements don't get generated in HTML.
When working with forms and you want to hide some fields, always go for [hidden]
to hide that element, destroying the element might give you problems:
import { CommonModule } from '@angular/common';
import { Component, inject } from '@angular/core';
import {
ReactiveFormsModule,
FormGroup,
FormBuilder,
FormsModule,
FormArray,
} from '@angular/forms';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';
@Component({
selector: 'app-root',
standalone: true,
imports: [FormsModule, ReactiveFormsModule, CommonModule],
template: `
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<div formArrayName="pages">
<div *ngFor="let page of pages.controls; let i = index" [formArrayName]="i">
<div [hidden]="i !== paginationIndex">
<div *ngFor="let myControl of setType(page).controls; let i = index" [formGroupName]="i">
{{myControl.get('name')?.value }}
<input type="text" formControlName="active">
</div>
</div>
</div>
</div>
<div>
<button class="btn" type="submit">Submit</button>
</div>
</form>
`,
})
export class App {
paginationIndex = 0;
name = 'Angular';
formBuilder = inject(FormBuilder);
form: FormGroup = new FormGroup({});
get pages() {
return this.form.get('pages') as FormArray;
}
setType(page: any) {
return page as FormArray;
}
ngOnInit() {
this.form = this.formBuilder.group({
pages: this.formBuilder.array([
this.formBuilder.array([
this.formBuilder.group({
active: true,
}),
this.formBuilder.group({
active: false,
}),
]),
this.formBuilder.array([
this.formBuilder.group({
active: true,
}),
this.formBuilder.group({
active: false,
}),
]),
]),
});
}
onSubmit() {}
}
bootstrapApplication(App);