angularangular-reactive-formsformarrayformgroups

Angular Reactive Forms with nested formGroups inside a formArray


I'm building a form with the structure below:

{
  "name": "root_test_name",
  "definition": {
    "name": "definition_test_name",
    "components": [
      {
        "name": "component_0_name",
        "info": {
          "name": "info_component_0_name"
        }
      },
      {
        "name": "component_1_name",
        "info": {
          "name": "info_component_1_name"
        }
      },
      {
        "name": "component_2_name",
        "info": { // <--Error on all attempts to set the formGroup name for this one :(
          "name": "info_component_2_name" 
        }
      }
    ]
  }
}

Using the reactive form, I'm able to display controls for all items, except the "name" inside the "info" formGroup. Stackblitz demo here

Here is my AppComponent:

import { Component, VERSION } from '@angular/core';
import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  name = 'Angular ' + VERSION.major;

  constructor(private fb: FormBuilder) {
    this.addComponent('component_0_name');
    this.addComponent('component_1_name');
    this.addComponent('component_2_name');
  }

  componentForm = this.fb.group({
    name: ['root_test_name', Validators.required],
    definition: this.fb.group({
      name: ['definition_test_name'],
      components: this.fb.array([]),
    }),
  });

  components(): FormArray {
    return this.componentForm.get('definition').get('components') as FormArray;
  }

  componentInfo(componentIndex: number): FormGroup {
    return this.components().at(componentIndex) as FormGroup;
  }

  addComponent(name: string) {
    let component = this.fb.group({
      name: [name],
      info: this.fb.group({
        name: ['info_' + name],
      }),
    });

    this.components().push(component);
  }
}

and here is the template:

<hello name="{{ name }}"></hello>
<p>Start editing to see some magic happen :)</p>

<div [formGroup]="componentForm">
  <input type="text" formControlName="name" />

  <div style="padding-left:10px" formGroupName="definition">
    <input type="text" formControlName="name" />

    <div>Components</div>
    <div formArrayName="components">
      <div *ngFor=" let component of components().controls; let componentIndex = index " >
        <div style="padding-left:10px [formGroupName]="componentIndex">
          <input type="text" formControlName="name" />

          <!-- 1st attempt to get to get name out of info formGroup 
          ERROR: Property 'info' does not exist on type 'AppComponent'-->
          <!-- <div [formGroupName]="info">
            <input type="text" formControlName="name" />
          </div> -->

          <!-- 2nd attempt
          ERROR: Type 'FormGroup' is not assignable to type 'string | number'-->
          <!-- <div style="padding-left:15px" [formGroupName]="componentInfo(componentIndex)" >
            <input type="text" formControlName="name" />
          </div> -->
        </div>
      </div>
    </div>
  </div>
</div>

<pre>{{ componentForm.value | json }}</pre>

How do I set the formGroup name for the "info" component so the "name" control shows value?


Solution

  • Use formGroupName instead of [formGroupName].

    <div formGroupName="info">
        <input type="text" formControlName="name" />
    </div>
    

    Sample Demo on StackBlitz


    Reference

    Angular - Register a nested FormGroup.