Recursive form (tree view) using Reactive forms in angular 6

有些话、适合烂在心里 提交于 2020-05-26 13:21:31

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!