Nested Form Array In Angular
Solution 1:
@Undefined, you need two different jobs
- Create a formGroup
- Display inputs that manage the formGroup
the first part is the easer. Go step by step, if you select template one, you need some like
this.fb.group({
template_name:"template one",
template_data:this.fb.array([
this.fb.group({
property_one:'',
property_two:''
})
])
})
but you want to do the things dinamically, so, make a function that receive an object and return a FormGroup. As you only need the "value" of the template and the childs, your function can be like
createFormGroup(value:string,children:any[]):FormGroup
{
/*e.g. for template one, you send
value: "Template one",
children: [
{ property_name: "Property one" },
{ property_name: "Property two" }
]
*/
let controls:FormGroup[]=children.map(
(x:any)=>this.fb.group({
[x.property_name]:''
})
)
return this.fb.group({
template_name:value,
template_data:this.fb.array(controls)
})
}
So yet we can create a formGroup for the differents templates and join in a FormArray
changeEvent(e) {
let arrayControls:FormGroup[] = [];
//in this.selectItems we have, e.g. [1,3]
for (let select of this.selectItems) {
//search the template, select will be e.g. 1,3
let template:any=this.templates.find(x=>x.key==select);
switch (+select) {
case 1:
arrayControls.push(this.createFormGroup(template.value,template.templateOneChild));
break;
case 2:
arrayControls.push(this.createFormGroup(template.value,template.templateTwoChild));
break;
case 3:
arrayControls.push(this.createFormGroup(template.value,template.templateThreeChild));
break;
}
}
this.form=this.fb.group({
template_details:this.fb.array(arrayControls);
})
}
See that if all ours children of templates was under a property "children" (not templateOneChild for the first, templateTwoChild for the seconds...) our function becomes in
changeEvent(e) {
let arrayControls:FormGroup[] = [];
//in this.selectItems we have, e.g. [1,3]
for (let select of this.selectItems) {
//search the template, select will be e.g. 1,3
let template:any=this.templates.find(x=>x.key==select);
arrayControls.push(this.createFormGroup(template.value,template.children));
}
this.form=this.fb.group({
template_details:this.array(arrayControls);
})
}
Well you have the "form" created, now is time to show it. The form is like
<div *ngIf="form">
<form [formGroup]="form">
<div formArrayName="template_details">
<div *ngFor="let item of details.controls;let i=index" [formGroupName]="i">
<input formControlName="template_name">
<div formArrayName="template_data">
<div *ngFor="let child of item.get('template_data').controls;let j=index" [formGroupName]="j">
<input formControlName="??????????">
</div>
</div>
</div>
</div>
</form>
</div>
Yes, we have a problem, we don't know the "formControlName" of the inner formArray. One solution is have a variable "controlsName" that will be an array of array, so, if e.g. we choose 1 and 3 template our controlsName was like
controlsName=[
["property_one","property_two"],
["property_six",property_seven"]
]
Well, again make a function that return an array of strings with the names of the properties. it's a simply version of our createFormGroup, receive "children" and return an array of strings.
getControlNames(children:any[]):string[]
{
let controlNames:string[]=children.map(x=>x.property_name);
return controlNames;
}
Well, in changeEvent we call to this function after call to createFormGroup
changeEvent(e) {
let arrayControls:FormGroup[] = [];
let controlsName:string[] = []; //<--add this line
for (let select of this.selectItems) {
let template:any=this.templates.find(x=>x.key==select);
switch (+select) {
case 1:
arrayControls.push(this.createFormGroup(template.value,template.templateOneChild));
controlsName.push(this.getControlNames(template.templateOneChild)); //<--and this
break;
... idem with case 2 and case 3...
}
}
this.controlsName=controlsName; //<--give value to name first
//then create the form
this.form=this.fb.group({
template_details:this.fb.array(arrayControls);
})
After this, replace the < input formControlName="??????????" > by
<input [formControlName]="controlsName[i][j]">
See that we use [formControlName] (not formControlName) because is an evaluated expression.
See the stackblitz here
Solution 2:
I am not sure about your question.You want to dynamically add controls using json.
Reference link : https://angular.io/guide/dynamic-form
Working example : https://stackblitz.com/edit/angular-srpk3w
Replace your files with the below code :
app.component.html
<select multiple [(ngModel)]="selectItems" (change)="changeEvent($event)">
<option *ngFor="let template of templates" [value]="template.key">{{template.value}}</option>
</select>
{{selectItems|json}}
<div *ngIf="form">
<form [formGroup]="form">
<div *ngFor="let item of array">
{{item.value}} is the parent
<div *ngFor="let child of item.templateChild; index as i">
{{child.property_name}}
<input type="text" formControlName="{{child.property_name.split(' ').join('_')}}" [value]="child.property_name" >
</div>
<br><br><br>
</div>
</form>
</div>
<br><br><br>
{{form.value|json}}
app.component.ts
import { Component } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
array: any[] = [];
selectItems: any;
form: FormGroup;
templates = [
{
key: 1, value: "Template one",
templateChild: [
{ property_name: "Property one" },
{ property_name: "Property two" }
]
},
{
key: 2, value: "Template two",
templateChild: [
{ property_name: "Property three" },
{ property_name: "Property four" },
{ property_name: "Property five" }
]
},
{
key: 3, value: "Template three",
templateChild: [
{ property_name: "Property six" },
{ property_name: "Property seven" }
]
}
]
changeEvent(e) {
this.array = [];
for (let select of this.selectItems) {
this.array.push(this.templates[select-1])
this.form=this.getFormValue(this.array);
}
}
getFormValue(array){
let group: any = {};
array.forEach(r=>{
r.templateChild.forEach((t,index)=>{
group[t.property_name.replace(/ /g, "_")]= new FormControl(t.property_name);
})
})
return new FormGroup(group);;
}
}
Post a Comment for "Nested Form Array In Angular"