8

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
  • Hi, your code has an infinite loop and it won't stop until you hit the call stack limit. This is almost always because of a recursive function with a base case that isn't being met. You need to do some dynamic rendering. Commented Nov 14, 2018 at 6:41

2 Answers 2

14
+50

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

Sign up to request clarification or add additional context in comments.

Comments

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.