Search code examples
angularangular-reactive-formsangular-forms

How to bind angular dynamic form?


I have a dynamic form items in component.

  itemFormGroup = new FormGroup({
    x: new FormControl(4),
    y: new FormControl(1),
    z: new FormControl(0),
  });

  itemsForm = new FormGroup({
    items: new FormArray([
      this.itemFormGroup
    ])
  });

I want to bind this lines for input text.

<table class="table table-borderless" [formGroup]="itemsForm">
    <thead>
      <tr>
        <th>x</th>
        <th>y</th>
        <th>z</th>
      </tr>
    </thead>
    <tbody formArrayName="items">
       <tr *ngFor="let item of items.controls; let i=index">
        <td><input type="text" class="form-control form-control-sm" [formControlName]="item.x"></td>
        <td><input type="text" class="form-control form-control-sm" [formControlName]="item.y"></td>
        <td><input type="text" class="form-control form-control-sm" [formControlName]="item.z" disabled></td>
      </tr>
    </tbody>
</table>

But this shows error on browser "Failed to compile."

Error: src/app/app.component.html:40:43 - error TS2339: Property 'items' does not exist on type 'AppComponent'.

40            <tr *ngFor="let item of items.controls; let i=index">
                                         

Solution

  • Angular is looking for items property in your component instance but can't find.

    You can create a getter like:

    get items() {
      return this.itemsForm.get('items') as FormArray;
    }
    

    Then replace your ngFor loop with the following:

    <tr *ngFor="let item of items.controls; let i=index" [formGroupName]="i">
      <td><input type="text" class="form-control form-control-sm" formControlName="x"></td>
      <td><input type="text" class="form-control form-control-sm" formControlName="y"></td>
      <td><input type="text" class="form-control form-control-sm" formControlName="z" [disabled]="true"></td>
    </tr>
    

    Note: I added [formGroupName]="i" to narrow context for Angular form and replaced [formControlName]="item.x" with string formControlName="x"

    If you don't want to use typed version you can get rid of get items() in component and just use:

    *ngFor="let item of $any(itemsForm.get('items')).controls; let i=index"