4

I have a table that is dynamically created and it displays data as follows:

<table>
  <tr *ngFor="let product of products">
    <td>{{product.name}}</td>
    <td>{{product.description}}</td>
    <td>{{product.value}}</td>
    <!-- BELOW IS WHERE A NEW VALUE WILL BE ENTERED -->
    <td><input type="text" value=""></td>
  </tr>
</table>

I've read that the appropriate way of handling this is with a FormsArray. But I've also read that the appropriate way of using a FormsArray is to grab its array of controls:

<table>
  <tr *ngFor="let product of this.form.get('productCollection').controls; let i = index;"
     [formGroupName]="i">
    <td>{{product.name}}</td>
    <td>{{product.description}}</td>
    <td>{{product.value}}</td>
    <!-- BELOW IS WHERE A NEW VALUE WILL BE ENTERED -->
    <td><input type="text" formControlName="name"></td>
  </tr>
</table>

The problem is that I don't have access to the description value here. And I haven't found a way to pass this as metadata to the control so I can show it.

So the question is for something like this, which is the correct approach? Is it FormArray? Is it an array of FormControls in one FormGroup? Or does each formcontrol need to be by itself? I'm open to suggestions on how to make this work.

user1100412
  • 463
  • 6
  • 18

2 Answers2

1

I'd loop through the actual product array in this case, not that controls array, since you need more info from the data than you do from the control.

Template

<form [formGroup]="form">
  <table formArrayName="productRows">
    <tr *ngFor="let product of products; let i = index;" [formGroupName]="i">
      <td>{{product.name}}</td>
      <td>{{product.description}}</td>
      <td><input type="text" formControlName="value"></td>
    </tr>
  </table>
</form>

Component

buildForm() {
  this.form = this.fb.group({
    productRows: this.fb.array(this.initProductRows())
  });
  this.form.valueChanges.subscribe((change) => {
    this.products.forEach((product, index) => {
      product.value = change.productRows[index].value;
    })
  });
}

initProductRows() {
  return this.products.map(product => {
    return this.fb.group({
      value: product.value
    });
  });
}

Part of the key here is initializing your FormArray in the beginning when building your form to be the same length (and have the same values) as the product data.

Also, I wasn't sure if you're trying to save the new value back into the original product data, but if so, then I added a valueChanges listener so that you can write it back. See the whole thing in the Stackblitz below.

https://stackblitz.com/edit/angular-edawnf

Scrimothy
  • 2,453
  • 11
  • 23
0

I think I might have found the answer. The key may be to NOT do a FormArray but rather an array of FormControls in a FormGroup. That way, I can continue to use the list with all the data it has, and then add a field based on the FormGroup. So, the end result would be:

<table>
  <tr *ngFor="let product of products">
    <td>{{product.name}}</td>
    <td>{{product.description}}</td>
    <td>{{product.value}}</td>
    <!-- BELOW IS WHERE A NEW VALUE WILL BE ENTERED -->
    <td>
      <div formGroupName="productCollection">
        <input type="text" formControlName="name">
      </div>
    </td>
  </tr>
</table>

If I'm wrong or if someone has a better way, by all means show it and let me know!

user1100412
  • 463
  • 6
  • 18