I have attached my code link. I tried create dynamic nested tree format form array. But I don't know how to create. I am new in angular framework. https://stackblitz.com/edit/angular-vytk36?file=src/index.html
-
Post your code here.possum– possum2023-04-19 13:45:20 +00:00Commented Apr 19, 2023 at 13:45
-
That's a lot of code. It would be better to add the part that is causing you trouble to the question.anon– anon2023-04-19 14:53:02 +00:00Commented Apr 19, 2023 at 14:53
-
You want to create a FormArray of a FormAray, see this SO or this another oneEliseo– Eliseo2023-04-19 14:53:38 +00:00Commented Apr 19, 2023 at 14:53
-
@Eliseo I am trying to data structures tree format form array. It is possible to create using angular form array?Arunkumar– Arunkumar2023-04-20 05:49:08 +00:00Commented Apr 20, 2023 at 5:49
-
1@Arunkumar, sure! (impossible is nothing) but it's a bit complex. I just added an answer. I hope can helpEliseo– Eliseo2023-04-21 06:44:11 +00:00Commented Apr 21, 2023 at 6:44
2 Answers
Always we have a "infinite level" is work to make a "recursive component"
A recursive component is only a component that call itself (see e.g. this SO)
Mannage a child component that mannage a formGroup from parent it's not very easy. We can use viewProviders or pass the FormGroup in an input (see this another SO)
We are going to choose pass the formGroup. But as our component is a recursive component we need in witch formGroup we are, so I use another input that it's the "name" of the formGroup. (remember that our formArrays are formArrays of formGroups. We need "reach" the formGroup
Puff...!
Before we are going to define a function to create the formGroup. We are export it because the same function can be used by the parent and by the component
export function setNestedMenu(data:any=null){
data=data || {name: "",displayname: "",id: "",nestedMenu: []}
return new FormGroup({
name:new FormControl(data.name),
displayName:new FormControl(data.name),
id:new FormControl(data.name),
nestedMenu:new FormArray(data.nestedMenu.map((x:any)=>this.setNestedMenu(x)))
})
}
NOTE: I use camelCase to give value to the "properties"
The component
@Component({
selector: 'form-component',
standalone: true,
imports: [CommonModule, FormsModule, ReactiveFormsModule],
template: `
<div [style.margin-left.px]="index*10" *ngIf="formGroup" [formGroup]="formGroup" >
<div formArrayName="nestedMenu">
<button (click)="addMenu()">add</button>
<div *ngFor="let group of menuArray?.controls;let i=index"
[formGroupName]="i">
<input formControlName="id"/>
<input formControlName="name"/>
<input formControlName="displayName"/>
<button (click)="removeMenu(i)">remove</button>
<button (click)="addSubMenu(i)">add submenu</button>
<ng-container *ngIf="getSubMenuArray(i).length">
<!--here we call again to our component-->
<form-component [form]="form"
[name]="name+'.'+i+'.nestedMenu'"
[index]="index+1" ></form-component>
</ng-container>
</div>
</div>
</div>`,
})
export class FormMenuComponent {
_index:number=0;
formGroup:FormGroup
@Input() form: FormGroup;
@Input() name:string="nestedMenu";
@Input('index') set _(value:number){
this._index=value;
this.formGroup=(value?this.form.get(this.name).parent:this.form) as FormGroup
}
get index()
{
return this._index
}
get menuArray() {
return this.form?this.form.get(this.name) as FormArray:null;
}
getSubMenuArray(index:number){
return this.form.get(this.name+'.'+index+'.nestedMenu') as FormArray
}
addMenu()
{
this.menuArray.push(setNestedMenu())
}
addSubMenu(index:number)
{
this.getSubMenuArray(index).push(setNestedMenu())
}
removeMenu(index:number)
{
this.menuArray.removeAt(index)
}
}
See how pass the name, the form and the index variables
Our main.component simply
<form-component [form]="form" [index]="0"></form-component>
form: FormGroup=new FormGroup({
nestedMenu:new FormArray([setNestedMenu()])
})
See stackblitz
NOTE: sorry for self quoting
3 Comments
The before answer pass to the form two variables: the "form" and the "name" of the formArray. This complicate the component. There're another better aproach that it's pass the "formGroup".
The first is that our component has an "setter" input like
formGroup:FormGroup
@Input('form') set __(value:AbstractControl){
this.formGroup=value as FormGroup
}
(It's necessary to "cast" the control)
Now we can use some like
<div *ngIf="formGroup" [formGroup]="formgroup">
...
<div formArrayName="nestedMenu">
<div *ngFor="let group of nestedMenuArray?.controls;let i=index"
[formGroupName]="i">
...
<ng-container *ngIf="getNestedMenuArray(i)?.length">
<!--see we pass as form the "formGroup"-->
<form-component [form]="nestedMenuArray.at(i)" [index]="index+1" >
</form-component>
</ng-container>
</div>
</div>
get nestedMenuArray() {
return this.formGroup?this.formGroup.get('nestedMenu') as FormArray:null;
}
getNestedMenuArray(index:number){
return this.nestedMenuArray?this.nestedMenuArray.at(index) as FormArray:null
}