user3100525
user3100525

Reputation: 13

Angular 8 Recursive Form

I'm trying to generate dynamic form recursively from JSON schema, but i'm struggling with form control not being found. Here is Code Examples.

I get this error

ERROR Error: Cannot find control with name: 'individualPerson'

I tried different approaches but there is still a problem. i know i miss something so please help. any help will be appreciated.

Problem occurs on the template side

app.components.ts

export class AppComponent {
  filterForm: FormGroup;
  filterFields: any[];

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.filterFields = [
      {
        key: "common",
        title: "main fields",
        group: [
          {
            key: "createdAt",
            title: "Create Date",
            type: "date"
          },
          {
            key: "individualPerson",
            title: "Physical Person",
            group: [
              {
                key: "firstname",
                title: "First Name",
                type: "text"
              },
              {
                key: "lastname",
                title: "Last Name",
                type: "text"
              },
              {
                key: "phone",
                title: "Phone Number",
                type: "text"
              },
              {
                key: "citizenshipCountry",
                title: "Country",
                type: "text"
              }
            ]
          },
          {
            key: "legalPerson",
            title: "Legal Person",
            group: [
              {
                key: "brandname",
                title: "Brand Name",
                type: "text"
              },
              {
                key: "fullname",
                title: "Full Name",
                type: "text"
              },
              {
                key: "phone",
                title: "Phone",
                type: "text"
              },
              {
                key: "registrationCountry",
                title: "Country",
                type: "text"
              }
            ]
          }
        ]
      }
    ];

    this.filterForm = this.generateFilterForm();
  }

  generateFilterForm(): FormGroup {
    const baseForm = this.fb.group({});
    this.filterFields.forEach(field => {
      baseForm.addControl(field.key, this.generateFormGroup(baseForm, field));
    });
    console.log(baseForm);
    return baseForm;
  }

  generateFormGroup(baseForm: FormGroup, field): FormGroup {
    if (field.group) {
      const formGroup = this.fb.group({});
      field.group.forEach(item => {
        formGroup.addControl(item.key, this.generateFormGroup(formGroup, item));
      });
      return formGroup;
    } else {
      baseForm.addControl(field.key, new FormControl(""));
    }
    return baseForm;
  }
}

app.component.html

<form [formGroup]="filterForm" class="filter-form">
    <ng-template #recursiveList let-filterFields let-fromGroup="fromGroup">
        <ng-container *ngFor="let item of filterFields">
            <ng-container *ngIf="item.group; else default;">
                <p>{{item.title}}</p>
                <div class="row pb-4" [formGroupName]="item.key">
                    <ng-container *ngTemplateOutlet="recursiveList; context:{ $implicit: item.group, fromGroup: {name: item.key} }"></ng-container>
                </div>
            </ng-container>
            <ng-template #default>       
                <div class="col-md-3">
                    <div class="form-group" [formGroupName]="fromGroup.name">
                        <input [type]="item.type" [formControlName]="item.key" [placeholder]="item.title" [name]="item.key" />
                    </div>
                </div>
            </ng-template>
        </ng-container>
    </ng-template>
    <ng-container *ngTemplateOutlet="recursiveList; context:{ $implicit: filterFields }"></ng-container>
</form>

Upvotes: 1

Views: 1242

Answers (1)

Eliseo
Eliseo

Reputation: 57939

there're some strange in your code

1.-Change your function generateFormGroup to return a simple FormControl in case field.group=false

generateFormGroup(baseForm: FormGroup, field): FormGroup|FormControl {
    if (field.group) {
      const formGroup = this.fb.group({});
      field.group.forEach(item => {
        formGroup.addControl(item.key, this.generateFormGroup(formGroup, item));
      });
      return formGroup;
    }
      return new FormControl("");
  }}

In recursive .html pass to the template the formGroup and use [formControl] and [formGroup] (I can not get it using formControlName and formGroupName). Some like -see that I changed a few where put the formGroup-

<form *ngIf="filterForm" [formGroup]="filterForm" class="filter-form">
    <ng-container *ngTemplateOutlet="recursiveList; 
          context:{ $implicit: filterFields,formGroup:filterForm }">
    </ng-container>
</form>

<ng-template #recursiveList let-filterFields let-formGroup="formGroup">
    <div class="form-group">
        <ng-container *ngFor="let item of filterFields">
            <p>{{item.title}}</p>
            <ng-container *ngIf="item.group; else default;">
                <div class="row pb-4">
                    <div [formGroup]="formGroup.get(item.key)">
                        <ng-container
                            *ngTemplateOutlet="recursiveList; 
                              context:{ $implicit: item.group, formGroup: formGroup.get( item.key)}">
                        </ng-container>
                    </div>
                </div>
            </ng-container>
            <ng-template #default>
                <div class="col-md-3">
                    <input [type]="item.type" [formControl]="formGroup.get(item.key)" 
                           [placeholder]="item.title" [name]="item.key" />
                </div>
            </ng-template>
        </ng-container>
    </div>
</ng-template>

You can see in the stackblitz

Upvotes: 2

Related Questions