Recursive Component of Dynamic Angular Forms - ERROR Error: Cannot find control with name:
C

1

2

I'm creating a project with a Reactive Form; based on Recursive Component that creates Dynamic Form from JSON file.

The Sources

This is an adaptation from Ionic based on Creating Dynamic Angular Forms with JSON

I Adapted the Recursive version procedures and other changes!

My code is located in Stackblitz.

I will show a reduced code version of json-form.component.html file:

<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
  <div fxLayout="column">
    <ng-container *ngFor="let control of jsonFormData?.controls">
      <div fxFlex="100%">
      
        <selects
          *ngIf="control.type === 'select'"
          [control]="control"
          [visible]="true"
        ></selects>
        
      </div>
    </ng-container>
  </div>
  <button mat-raised-button class="mt-1" color="primary">
    <em class="fa fa-save">Submit</em>
  </button>
</form>

As you can see the custom component is selects.

Now, let's take a look at the Recursive code of use of the selects template. Again I reduce the code of select.component.html file:

<form [formGroup]="form">
  <ng-container *ngIf="control?.children">
    <mat-form-field
      *ngIf="control.type === 'select' && control.visible"
      fxFlex="100%"
      hideRequiredMarker
    >
      <mat-label>{{ control.label }}</mat-label>
      <mat-select
        [formControlName]="control.name"
        [placeholder]="control.label"
        (selectionChange)="onSelectChange($event.value)"
      >
        <mat-option
          *ngFor="let child of control.children"
          [value]="child.value"
        >
          {{ child.label }}
        </mat-option>
      </mat-select>
    </mat-form-field>
  </ng-container>

  <ng-container *ngFor="let child of control?.children">
    <div fxFlex="100%">
      <selects *ngIf="child.type === 'select'" [control]="child"></selects>
    </div>
  </ng-container>
  
</form>

The code of recursion using selects Component is:

  <ng-container *ngFor="let child of control?.children">
    <div fxFlex="100%">
      <selects *ngIf="child.type === 'select'" [control]="child"></selects>
    </div>
  </ng-container>

An example of error is:

ERROR
Error: Cannot find control with name: 'Petitioner (C2 -> P2)'

Unfortunately, I can't to find the problem in order to solve it.

Some clue in order to solve the error?

EDIT I suspect that not all components are shown inmediately, only when the Select is clicked; then the component is not still created.

Califate answered 20/1, 2022 at 2:4 Comment(5)
Firstly, you need to understand the shape of your root FormGroup value, then generate that FormGroup with nested controls. I don't understand the most nested config: why do you use siblings and not children and it doesn't have name propertyMissile
@Missile siblings are not nested Select Component. For this example can be ignored. Sorry, I didn't catch Firstly, you need to understand the shape of your root FormGroup value. I try to understand, I'm trying to relate the FormGroup of SelectComponent using FormGroupDirective directive.Califate
You have created only top level FormGroup and always refer to that root FormGroup.Missile
@Missile Thank yoy very mych for your time. Now, I need to know how to fix it!.Califate
Here other example dev.to/julianobrasil/…Imprescriptible
C
0

As you can see in the displayed view, all the FormControl are there. But, when a click event is presented. I show the all FormControl's of my Form in that istant with the line: console.log(this.form.value);

As you can see in the Green bottom right rectangle in. In the last Event. Only appears two controls!!!!, But are shown 7 formControls without button!... Then my Form (variable this.form) need to add those FormControl's.

enter image description here

How to do that?

First, I need to obtain the main Form and FormGroup (of my parent Component).

In my parent component (JsonFormComponent) from the json-form.component.ts file:

export class JsonFormComponent implements OnChanges {
  @Input() jsonFormData: JsonFormData;

  public myForm: FormGroup = this.fb.group({});
  public myFormBuilder: FormBuilder;

  constructor(private fb: FormBuilder) {
    this.myFormBuilder = fb;
  }

Now, I have public myForm: FormGroup... and public myFormBuilder: FormBuilder; for my parent Component!.

The next step, is create local variable in each child-component located in the selects.component.ts file.

export class SelectsComponent implements OnInit {
  public form: FormGroup;
  @Input() formBuilder: FormBuilder;

  constructor(private rootFormGroup: FormGroupDirective) {}

  ngOnInit(): void {
    this.form = this.rootFormGroup.form;
  }

Now, I have public form: FormGroup... and @Input() formBuilder: FormBuilder; for my child Component!.

The next step, is to pass that objects to my child-components:

The solution in order to pass (or relate) myForm (FormGroup) comes from this post. But, the relation for myFormBuilder is done in the json-form.component.html file.

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

Specifically in the line: [formBuilder]="myFormBuilder".

And for the recursion, in the selects.component.html file

  <ng-container *ngFor="let child of control?.children">
    <div fxFlex="100%">
      <selects
        *ngIf="child.type === 'select'"
        [control]="child"
        [formBuilder]="formBuilder"
      ></selects>
    </div>
  </ng-container>

Specifically in the line: [formBuilder]="formBuilder".

Now, in my child-component in the file selects.component.ts I created this method:

  private newControl(control: JsonFormControls) {
    this.form.addControl(
      control.name,
      this.formBuilder.control(control.value, getValidators(control))
    );
    control.visible = true;
  }

Lastly: I changed my method, in order to add the FormControl that needs to be displayed:

  public onSelectChange(event: MatSelectChange) {
    console.log(this.form.value);
    this.control.value = event + '';
    if (this.control.children) {
      this.recursiveConcealer(this.control.children);
      const child = this.control.children.find(
        (child) => child.value === event + ''
      );
      this.newControl(child);
      if (child.siblings) {
        for (let sibling of child.siblings) {
          this.newControl(sibling);
        }
      }
    }
  }

Note specifically the lines: this.newControl(child); and this.newControl(sibling);.

As you can see in the Blue rectangle, the errors are gone.

enter image description here

NOTE: Theres is not the only one solution, here a question How can I pass the FormGroup of a parent component to its child component using the current Form API about this subject.

Califate answered 20/1, 2022 at 23:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.