问题
I have understand concept of recursive elements(tree view) from below.
Link 1
In my case, I want to use it with forms; let's say simple text input recursively. JSON structure of form is as below.
JOSN Structure
I have prepared below code.I am getting Maximum call stack size exceeded error by executing below code.
Below is my component.html file.
<form [formGroup]="testForm" (ngSubmit)="onSubmit()">
<div formArrayName="element">
<ng-template #recursiveList let-list>
<div *ngFor="let item of testForm.get('element').controls;let i=index;">
<div [formGroupName]="i">
<input type="text" formControlName="type">
</div>
<!-- {{item.get('element')?.controls?.length}} -->
<div *ngIf="item.get('element')?.controls?.length > 0">
<ng-container *ngTemplateOutlet="recursiveList; context:{ $implicit: item.get('element').controls }"></ng-container>
</div>
</div>
</ng-template>
<ng-container *ngTemplateOutlet="recursiveList; context:{ $implicit: testForm.get('element').controls }"></ng-container>
</div>
And here is component.ts file.
import { Component, OnInit } from '@angular/core';
import {FormControl, FormGroup, Validators, FormBuilder, FormArray} from '@angular/forms';
@Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: [
'./test.component.scss'
]
})
export class TestComponent implements OnInit{
testForm:FormGroup;
element:any;
constructor(private formBuilder: FormBuilder) { }
ngOnInit() {
this.testForm=this.formBuilder.group({
element:this.formBuilder.array([
this.formBuilder.group({
type:'',
element:this.formBuilder.array([
this.formBuilder.group({
type:'',
element:this.formBuilder.array([
])
})
])
})
])
})
}
onSubmit() {
console.log(this.testForm.value);
}
}
回答1:
In fact, Bhavik Patel's answer will solve the problem with the error:
Maximum call stack size exceeded
But your form bindings won't work properly due to how Angular reactive forms work. It relies on Dependency Injection tree which for this template looks like:
FormGroup(testForm)
|__ FormArrayName('element')
|___ FormGroupName('0')
| ....
|___ FormGroupName(n)
You will always get updates only on your top level controls.
To fix this issue you can define some prefix which will be updated inside embedded view. And then use that prefix to define formGroup
on each level of your tree:
<form class="tree" [formGroup]="testForm" (ngSubmit)="onSubmit()">
<ng-template #recursiveList let-controls let-prefix="prefix">
<ng-container *ngFor="let item of controls; let i = index">
<div class="tree-item" [formGroup]="testForm.get(prefix + i)">
<input type="text" formControlName="type">
</div>
<div class="sub-tree" *ngIf="item.get('element')?.controls?.length">
<ng-container
*ngTemplateOutlet="recursiveList; context:{ $implicit: item.get('element').controls, prefix: prefix + i + '.element.' }"></ng-container>
</div>
</ng-container>
</ng-template>
<ng-container
*ngTemplateOutlet="recursiveList; context:{ $implicit: testForm.get('element').controls, prefix: 'element.' }"></ng-container>
</form>
Ng-run Example
回答2:
There are some changes in your HTML code as you are looking for the element list instead of list property itself.
Here is the updated code after resolving the issue.
component.html
<form [formGroup]="testForm" (ngSubmit)="onSubmit()">
<div formArrayName="element">
<ng-template #recursiveList let-element>
<div *ngFor="let item of element;let i=index;">
<div [formGroupName]="i">
<input type="text" formControlName="type">
</div>
<!-- {{item.get('element')?.controls?.length}} -->
<div *ngIf="item.get('element')?.controls?.length > 0">
<ng-container *ngTemplateOutlet="recursiveList; context:{ $implicit: item.get('element').controls }"></ng-container>
</div>
</div>
</ng-template>
<ng-container *ngTemplateOutlet="recursiveList; context:{ $implicit: testForm.get('element').controls }"></ng-container>
</div>
</form>
P.S.: And of course you should refactor your ts code as well. It should be dynamically generated.
来源:https://stackoverflow.com/questions/52961297/recursive-form-tree-view-using-reactive-forms-in-angular-6