1

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.

1
  • 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 structure Commented May 19, 2018 at 7:34

2 Answers 2

2
+50

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

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks this is what I was looking for, I've awarded you the bounty. How would I structure the form so that the group name was included in the JSON form value so that the form value looked similar to the original JSON data that is being pulled in?
Glad it helped. Well if the group name is not supposed to be editable, I'd just advise you to stick with that solution. It's fine :) It's just a bit different than what you can find in tutorials or doc where they are using data from the form only. If you really want to put that into the form, you'd have to create an intermediate form group as the name is common to multiple products. It'd just had more complexity for nothing.
0

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

1 Comment

This only provides one set of controls for each grouping where there should be multiple formarrays in each formgroup

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.