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.

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 property Commented Jan 20, 2022 at 3:03
  • @yurzui 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. Commented Jan 20, 2022 at 3:36
  • You have created only top level FormGroup and always refer to that root FormGroup. Commented Jan 20, 2022 at 13:48
  • @yurzui Thank yoy very mych for your time. Now, I need to know how to fix it!. Commented Jan 22, 2022 at 12:13
  • Here other example dev.to/julianobrasil/… Commented Jan 29, 2022 at 4:34

1 Answer 1

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.

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

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.