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);
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With