1
votes

I have a Form Array that I would like to duplicate for each iteration of an *ngFor loop. My form is setup in the model as follows:

initProducts() {
    return this.fb.group({
      begTally: ['', Validators.required],
      endTally: ['', Validators.required],
    }) 
  }

  ngOnInit() {
    this.productionForm = this.fb.group({
      products: this.fb.array([
        this.initProducts()
      ])
    })
  }

When I print out the form in the view with {{ myform.value | json }} I am only seeing one iteration of the form array. Here's a StackBlitz with the full setup. I would also like to patch in the values to the form controls from my prodData json. Not sure what I'm doing wrong.

2
You want to add an array for every item in an array, so you should have an array of an array of items in your form structureJusmpty

2 Answers

2
votes

Within your Stackblitz example you were not that far!

Here's my proposal:

this.productionForm = this.fb.group({
  production: this.fb.array(
    this.prodData
      // for each...
      .groups
      .reduce((acc, group) => [
        ...acc,
        // ...product of each group
        ...group.products.map(product =>
          // create a form group
          this.fb.group({
            begTally: [product.begTally, Validators.required],
            endTally: [product.endTally, Validators.required],
          })
        )
      ], [])
  )
})
  • to create a form array, you need to wrap it into a form group (here called production)
  • then loop on the groups using reduce, which allows you to then loop on every products from every groups
  • build a form group for each of them

In the view, it's a little bit more tricky as you want to access data that are not stored into the form. So we have to mix between original data and the ones within our form:

<form [formGroup]="productionForm">
  <table *ngFor="let group of prodData.groups; let i = index">
    <thead>
      <th class="group-name">
        <span>{{group.name}}</span>
      </th>
      <th>Beginning Tally</th>
      <th>Ending Tally</th>
      <th>Total</th>
    </thead>

    <tbody formArrayName="production">
      <tr *ngFor="let product of group.products; let j=index" [formGroupName]="i + j">
        <td>{{product.name}}</td>

        <td>
          <input type="number" formControlName="begTally">
        </td>

        <td>
          <input type="number" formControlName="endTally">
        </td>

        <td>
          {{ product.begTally + product.endTally }}
        </td>
      </tr>
    </tbody>
  </table>
</form>

Here's my fork of your Stackblitz with a working example:
https://stackblitz.com/edit/angular-kvlxnp?file=src%2Fapp%2Fapp.component.ts

0
votes

You must have the same number of groups in form as in markup. For example

this.productionForm = this.fb.group({
  products: this.fb.array([
    this.initProducts(),
    this.initProducts(),
    this.initProducts()
  ])
})

will fix the issue, which means you have to interate the same collection that *ngFor iterates, and create the same ammount of FormControls