0

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

AkiS
  • 47
  • 2
  • 9
  • I would like the dynamic component to be independent of the parent form and only know about the groupOfGroups sub form group. For example use ControlValueAccessor instead of ControlContainer. Maybe I got this wrong, I'm not sure – AkiS Dec 19 '21 at 09:32
  • @AkiS When You want to bind formGroup of child to parent component form then it will not be independent at all, Yes you can handle child component logic independently but can not say any group is independent , assigned and grouped with parent formGroup. – GRD Dec 19 '21 at 10:54
  • Ok maybe I did not describe it correctly. Ofc it will belong to the parent form. What I want is to only reference the specific form group from my dynamic component. In another use case this dynamic component could be used in a differently structured form and the groupOfGroups could be nested multiple levels in. But my main problem is that I cannot make the form of the dynamic linked to parent one way or another – AkiS Dec 19 '21 at 11:22

1 Answers1

1

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>
Jason White
  • 5,495
  • 1
  • 21
  • 30
  • 1
    This works indeed. I had a typo in my code (an extra [] on `groupOfGroups: [this.fb.group....]`) which caused the groupOfGroups FormGroup to be considered FormControl. Anyway I will mark this as the answer, however I would be interested to see a solution using ControlValueAccessor instead of ControlContainer. Thank you a lot – AkiS Dec 19 '21 at 18:30