Angular 2 form with array of object inputs

16,617

The problem here is in the input name, in your case you are using name = "description", what is happening here is that is always is going to upadte the form.description.value with the last description. In your case you have array of descriptions, you need to have array of form.description[i].value

So the fix is change description to be unique.

name="description_{{i}}"

Repeat this for every input inside a ngFor. For fixing this issue you can do this:

<ul class="list invoice-table__body">
  <li *ngFor="let item of invoice.lineItems; let i = index">

    <input
      type="text"
      name="description_{{i}}"
      class="col-sm-8"
      [(ngModel)]="invoice.lineItems[i].description">

    <input
      type="number"
      name="quantity_{{i}}"
      class="col-sm-2"
      [(ngModel)]="invoice.lineItems[i].quantity">

    <input
      type="number"
      name="total_{{i}}"
      class="col-sm-2"
      [(ngModel)]="invoice.lineItems[i].total">
  </li>
</ul>

you can see your example working here: https://plnkr.co/edit/orldGCSYDUtlxzMFsVjL?p=preview

My recommendation is always works with ReactiveForms (Model Driven Forms), maybe the only case i will use FormsModule (Template Driven Forms) is for small form. Its Easier and its better for doing arrays of data.

Hope its solved your issue.

Share:
16,617
justinledouxweb
Author by

justinledouxweb

Updated on June 23, 2022

Comments

  • justinledouxweb
    justinledouxweb almost 2 years

    I'm building an invoicing app to learn Angular2. The issue I am hitting is how to build the line item component where a line contains 3 inputs that should come from and bind to an object in an array of line item.

    In angular 1, I can easily achieve this by adding an ng-form directive to the container of the inputs. What is the equivalent here?

    Here is my code:

    HTML:

    <form name="form" ng-submit="$ctrl.submit(form)" novalidate>
    
    <!-- some more code -->
    
    <ul class="list invoice-table__body">
      <li *ngFor="let item of model.lineItems; let i = index">
        <input
          type="text"
          name="description"
          class="col-sm-8"
          [(ngModel)]="item.description">
    
        <input
          type="number"
          name="quantity"
          class="col-sm-2"
          [(ngModel)]="item.quantity">
    
        <input
          type="number"
          name="total"
          class="col-sm-2"
          [(ngModel)]="item.total">
      </li>
    </ul>
    
    <!-- some more code -->
    
    </form>
    

    Component:

    import { Component } from '@angular/core';
    import { Invoice } from './invoice.model';
    import { InvoiceLineItems } from './invoice-line-item.model';
    
    @Component({
      selector: 'create-invoice',
      templateUrl: 'create-invoice/create-invoice.html'
    })
    export class CreateInvoiceComponent {
      model: Invoice = new Invoice(
        85,
        'CAD',
        null,
        [ // lineItems
          new InvoiceLineItems('Web development for BAnQ'),
          new InvoiceLineItems('Sept 1 to 3', 14, 910),
          new InvoiceLineItems('Sept 5 to 10', 34, 5293),
          new InvoiceLineItems('Sept 11 to 20', 24, 5293),
          new InvoiceLineItems('Sept 21 to 38', 11, 2493),
        ],
        13989,
        100,
        200,
        15000,
        '',
        null,
        '$'
      );
    
      getTotal(): number {
        return this.model.lineItems.reduce(
          (a, b): number => a + (isNaN(b.total) ? 0 : b.total),
          0);
      }
    }
    
  • bobbyg603
    bobbyg603 over 6 years
    Do you have an example of how you would do this with a reactive form?
  • Armatorix
    Armatorix over 6 years
    Thx for this answer, but I would change one thing: [(ngModel)]="item.description" etc.
  • Malik
    Malik over 5 years
    stackoverflow.com/a/47567879/1305026 more information on this exact problem can be found in this answer.