Recursive Dynamic Angular Form based on ng-template, binding missing with Component

556 views Asked by At

I was creating this project (Source Project) https://stackblitz.com/edit/angular-ivy-wkshmk; I had error but I 'solve' (with problems) here https://stackoverflow.com/a/70794789/811293 The problems are: Validations aren't working at Select level 2 or more.

I was checking this answer https://stackoverflow.com/a/56191376/811293 for this question.

Then I was trying to change my solution to based on ng-template, ng-container and ngTemplateOutlet

The transition is in this project: https://stackblitz.com/edit/angular-ivy-gns1kn

In the json-form.component.html file (source project) I changed

    <selects
      *ngIf="control.type === 'select'"
      [control]="control"
      [visible]="true"
      (addControl)="onAddControl($event)"
      [formBuilder]="myFormBuilder"
    ></selects>

To (destiny project) in the same json-form.component.html file

      <ng-container
        *ngIf="control.type === 'select'"
        [ngTemplateOutlet]="selects"
        [ngTemplateOutletContext]="{
          incomingControl: control,
          incomingVisible: true
        }"
      >

I removed the SelectsComponent (selects.component.ts and selects.component.html) and I changed to ng-template

<ng-template
  #selects
  let-control="incomingControl"
  let-visible="incomingVisible"
>
...
</ng-template>

The recursion in the selects.component.html file (source project) for selects was

  <selects
    *ngIf="child.type === 'select'"
    [control]="child"
    [parentControl]="control"
    [formBuilder]="formBuilder"
  ></selects>

was moved to (destiny project) in the same json-form.component.html file

      <ng-container
        *ngIf="child.type === 'select'"
        [ngTemplateOutlet]="selects"
        [ngTemplateOutletContext]="{
          incomingControl: child,
          parentControl: control
        }"
      >
      </ng-container>

NOTE:

Due to the source project has a longer json with a deeper nested(recursion) , I changed to a shorter json in the destiny project. This change can be done in the home.page.ts file, changing the needed line from .get('/assets/company.json') to .get('/assets/nested-select.json').

I would like to show the nested selects when the click is performed as the source project

enter image description here

THE PROBLEM:

enter image description here

As you can see in the above image; (at left side in the json file) the first component with the name company has children! In the display view yo can see: "control.children: ...."

That is shown due to lines 100 and 101 in the json-form.component.html file.

      control.children:<br />
      {{ control.children | json }}<br />

But, When the click event is performed, the chilren are missing in the component side!!! As you can check in the image.

In the component at from line 83 through line 85 json-form.component.ts the file

console.log(
  this.control?.children.length > 0 ? 'with children' : 'without children'
);

QUESTIONS:

What is the reason to lost the binding between the Component and Template?

How solve the problem with initial control (company)?

1

There are 1 answers

4
Siddhant On

What is the reason to lost the binding between the Component and Template?

In the JsonFormComponent, you have declared control as an @Input binding which is undefined as you do not have any control input binding below:

<app-json-form [jsonFormData]="formData" [visible]="true"></app-json-form>

control.children | json here control refers to one of the controls within jsonFormData, as you are looping through it's controls via *ngFor directive. Whereas within the component onSelectChange() method, you are referring to this.control which is undefined as explained earlier.

It worked in your source project, since you did pass control via input property binding to the SelectsComponent and used the same within it's template too.

<selects
  *ngIf="control.type === 'select'"
  [control]="control"
  [visible]="true"
  (addControl)="onAddControl($event)"
  [formBuilder]="myFormBuilder"
></selects>

How solve the problem with initial control (company)?

To get hold of the control within onSelectChange method, you can pass the control reference from the template and use the same in the method instead of this.control.

(selectionChange)="onSelectChange(control, $event.value)"

enter image description here


Edit: For displaying subsequent fields, made below changes in json-form.component.html

  • Removed <br> element from line 8 (Not mandatory, but the screenshot shown has this change)
  • Modified line 38 to pass correct context object properties to ng-template

enter image description here