Home > Enterprise >  How to bind form group to nested dynamically created components in Angular
How to bind form group to nested dynamically created components in Angular

Time:12-20

In my Angular 12 app I have this type of hierarchy:

- app.component
-- Parent component with form (reactive)
--- Component that creates the dynamic components
---- Dynamic component

and my formBuilder form looks like this:

    form = this.fb.group({
        baseLevelControl: ["base control"],
        groupOfGroups: [this.fb.group({
            innerGroup1: this.fb.group({
                dummy1Control1: "control 1 from inner group 1",
                dummy1Control2: "control 2 from inner group 1",
            }),
            innerGroup2: this.fb.group({
                dummy2Control1: "control 1 from inner group 2",
                dummy2Control2: "control 2 from inner group 2",
            }),
        })]
    });

My dynamic component only needs to know about the groupOfGroups and not the whole form because it will be used in many places with different kinds of forms.

When I set in the dynamic component

<div [formGroup]="innerForm">

I get a seperate independent form that does not communicate with parent form. If I use

<div formGroupName="innerForm">

I get error that formGroupName must be used with a parent formGroup directive

How can I properly bind the groupOfGroups?

I've create this stackblitz project to emulate the issue, any help would be much appreciated

CodePudding user response:

You can inject ControlContainer in your child components to get access to the parent components form group.

In your child dynamic1.component.ts component, inject ControlContainer and assign the parent form group to the child component's FormGroup property. This will give you access to the parent FormGroup from the parent component.

@Component({
  selector: 'app-dynamic1',
  templateUrl: './dynamic1.component.html',
  styleUrls: ['./dynamic1.component.scss'],
})
export class Dynamic1Component implements OnInit {
  public formGroup: FormGroup;

  constructor(private _parentContainer: ControlContainer) {}

  ngOnInit(): void {
    this.formGroup = this._parentContainer.control as FormGroup;
  }
}

Update your dynamic1.component.html to use this form group.

<div *ngIf="formGroup" [formGroup]="formGroup">
  <div formGroupName="innerGroup1">
    <p>dummy controls for group 1</p>
    <input formControlName="dummy1Control1" />
    <input formControlName="dummy1Control2" />
  </div>
  <div formGroupName="innerGroup2">
    <p>dummy controls for group 2</p>
    <input formControlName="dummy2Control1" />
    <input formControlName="dummy2Control2" />
  </div>
</div>

In your level1.component.ts update the FormGroup. You originally had the groupOfGroups as an array. This is not needed, you can simply just assign a FormGroup to this.

@Component({
  selector: 'app-level1',
  templateUrl: './level1.component.html',
  styleUrls: ['./level1.component.scss'],
})
export class Level1Component implements OnInit {
  form = this.fb.group({
    baseLevelControl: ['base control'],
    groupOfGroups: this.fb.group({
      innerGroup1: this.fb.group({
        dummy1Control1: 'control 1 from inner group 1',
        dummy1Control2: 'control 2 from inner group 1',
      }),
      innerGroup2: this.fb.group({
        dummy2Control1: 'control 1 from inner group 2',
        dummy2Control2: 'control 2 from inner group 2',
      }),
    }),
  });

  // OTHER STUFF....

}

And your level1.component.html template would look like...

<h1>Level 1</h1>
<div [formGroup]="form">
  <label for="baseControl">Base Control Value:</label>
  <input name="baseControl" formControlName="baseLevelControl" type="text" />
  <div formGroupName="groupOfGroups">
    <h3>Dynamic 2</h3>
    <app-dynamic1></app-dynamic1>
  </div>
  <div>
    <button type="button" (click)="onSubmit()">Submit</button>
  </div>
</div>
  • Related