Ricardo Patrick
Ricardo Patrick

Reputation: 81

Angular input form into *ngFor

I'm little confused about how I should work with Angular 2 when I need <input> tags inside the <tr> tags of a table, when the <tr> tags are generated by a *ngFor. My situation is this: I have an array of "products", I should show the product's info on <tr> tags and, for each product, an input field to increase the stock of each product. This is what I have done:

ProductStokeComponent.ts

export class ProductStokeComponent implements OnInit {

  form: FormGroup;
  products: Subject<Product[]> = new Subject();

  constructor(
    private productService: ProductService,
    private formBuilder: FormBuilder,
  ) { }

  ngOnInit() {
    this.formInit();
    this.getProducts();
  }

  formInit() {
    this.form = this.formBuilder.group({
      products: this.formBuilder.array([])
    });
  }

  getProducts() {
    this.productService.getProducts().subscribe(data => {
      this.products.next(data);
    });
  }

}

ProductStokeComponent.html

<form [formGroup]="form" (ngSubmit)="formSubmit()">
  <table>
    <thead>
      <tr>
        <th>ID</th>
        <th>Name</th>
        <th>Stock</th>
        <th>Stock Entrance</th>
      </tr>
    </thead>
    <tbody formArrayName="products">
      <tr *ngFor="let product of products| async">
        <td>{{ product.id }}</td>
        <td>{{ product.name }}</td>
        <td>{{ product.stock }}</td>
        <td>
          <input type="number" name="stock[product.id]" >
        </td>
      </tr>
    </tbody>
  </table>
  <button type="submit">Add Stock</button>
</form>

On pure html, I was able to do away with a named array, but with Angular 2 I'm trying using a reactive form, but then I should use a for loop to fill the form after a loop to generate the table for each product, this sounds like using a lot of resources to do a simple thing.

Update with methods reactive form

ProductStokeComponent.ts

ngOnInit() {
    this.formInit();
    this.getProducts();
}

formInit(product?: Product) {
    this.form = this.formBuilder.group({
      products: this.formBuilder.array([])
    });
}

ProductStokeComponent.html

<tbody formArrayName="products">

Upvotes: 0

Views: 2147

Answers (1)

DWilches
DWilches

Reputation: 23015

The reactive solution looks like this:

    <tr *ngFor="let product of form.controls['products'].controls">
        <td>{{ product.value.id }}</td>
        <td>{{ product.value.name }}</td>
        <td>{{ product.value.stock }}</td>
        <td>
            <input #moreStock>
            <button (click)="addMoreStock(product.value, moreStock.value)">+</button>
        </td>
    </tr>

Or if you want the template forms solution:

<tbody>
  <tr *ngFor="let product of products">
    <td>{{ product.id }}</td>
    <td>{{ product.name }}</td>
    <td>{{ product.stock }}</td>
    <td>
      <input #moreStock>
      <button (click)="addMoreStock(product, moreStock.value)">+</button>
    </td>
  </tr>
</tbody>

In both cases, in your TypeScript file add a method like this:

public addMoreStock(product, howMuch)
{
    product.stock += parseInt(howMuch, 10);
}

Upvotes: 1

Related Questions