I have this html code, in this code I used [(ngModel)] for change my value input, and when I change I want to calculate automatic my Total, subtotal and Amound Paid. Maybe this is not good solution because I have some problem.
Please I need help.
My html code
<form [formGroup]="addsale" (ngSubmit)="onaddsale()">
<table align="center" class="table table-bordered table-hover">
<thead>
<tr style="color:black;">
<th>Unit_price</th>
<th>Quantity</th>
<th>Description</th>
<th>Subtotal</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr class="group" style="cursor: pointer" *ngFor="let item of products; let i = index">
<td>
<input formControlName="Unit_price" id="Unit_price " type="number" class="validate"[(ngModel)]="item.Unit_price">
</td>
<td>
<input formControlName="Quantity" id="Quantity " type="number" class="validate" [(ngModel)]="item.Quantity">
</td>
<td>
<input type="text" formControlName="Description" id="Description" name="Description" [(ngModel)]="item.Description">
</td>
<td>
<input formControlName="Subtotal" id="Subtotal" type="number" class="validate" [(ngModel)]="item.Subtotal">
</td>
<td>
<button type="button" class="fa" (click)="onDelete(i)">x</button>
</td>
</tr>
</tbody>
</table>
<br>
<br>
<div class="row">
<div class="input-field col s2" style="float: right;">
<label for="total">Total {{total}} ALL</label>
<input formControlName="total" id="total" type="text" class="validate" [(ngModel)]="total">
</div>
<div class="input-field col s2" style="float: right;">
<label for="amount_paid">Amount Paid:</label>
<input formControlName="amount_paid" id="amount_paid" [(ngModel)]="total" type="text" class="validate">
</div>
<div class="input-field col s2" style="float: right;">
<label for="total">Subtotal</label>
<input formControlName="Subtotal" id="Subtotal" type="text" class="validate" [(ngModel)]="total">
</div>
</div>
<hr>
<br>
<div id="add_homebox_button_container" class="row" style="float: right;">
<button id="add_client_button" type="submit" class="btn waves-effect waves-light">
Register
</button>
</div>
</form>
Code ts:
export class AddSaleFormComponent implements OnInit {
addsale: FormGroup;
loading: boolean = false;
client: Client[];
producttype: ProductType[];
contrat: Contrat[];
gpss: GPS[];
homeboxp: HomeboxP[];
sensors: Sensors[];
homebox: Homebox[];
products: Products[] = [];
Price: number = 0;
Total;
variable: any;
Quantity: number;
Unit_price: number;
selectedClient: Client = new Client('');
@Input() selectedProduct: Products;
constructor(
private router: Router,
private fb: FormBuilder,
private ss: SalesService,
private ps: ProductsService,
private cs: ClientService,
private pts: ProducttypeService,
private css: ContratService,
private gps: GpsService,
private hbp: HomeboxpackageService,
private sensor: SensorsService,
private hb: HomeboxService
) {
this.addsale = this.fb.group({
'invoice_number': new FormControl('', [Validators.required, Validators.nullValidator]),
'invoice_date': new FormControl('', Validators.required),
'client_id': new FormControl('', Validators.required),
'amount_paid': new FormControl('', Validators.required),
'notes': new FormControl('', Validators.required),
'Subtotal': new FormControl('', Validators.required),
'products': this.fb.array([]),
'total': new FormControl('', Validators.required),
'contactNo': new FormControl('', Validators.required),
'address': new FormControl('', Validators.required),
'Unit_price': new FormControl('', Validators.required),
'Quantity': new FormControl('', Validators.required),
'Description': new FormControl('', Validators.required),
'line_num': new FormControl('', Validators.required)
});
}
ngOnInit() {
this.products = this.ps.getProduct();
console.log(this.products)
this.pts.getAllProductType().subscribe(
producttype => {
this.producttype = producttype;
}
);
}
onaddsale() {
this.loading = true;
let sale = this.addsale.value
sale.products = this.products
let newSale = new Sale(sale);
console.log(newSale)
this.ss.saleitemcreate(newSale).subscribe(
result => {
if (result === true) {
Materialize.toast('Sale saved successfully', 4000);
} else {
this.loading = false;
}
},
error => {
this.loading = false;
}
);
}
get total() {
let Total = 0;
for (let p of this.products) {
Total += p.Unit_price * p.Quantity;
}
return Total;
}
Subtotal() {
let subtotal = 0;
for (let p of this.products) {
subtotal = p.Unit_price * p.Quantity;
}
console.log(subtotal)
return subtotal;
}
}
Your problem is the
FormGroup
. As you can see, you've got oneFormControl
for 'Unit_price'. However, in your template, you've got several rows, and in each row there is an input that points to that sameFormControl
(via theFormControlName
directive). But they all belong to the sameFormGroup
, and that messes with the form, so all the inputs with the same name refer to the sameFormControl
, causing the odd behaviour you're suffering. You can't have duplicate controls under the sameFormGroup
directive.What you've got to do is define a
FormGroup
for each row of the table. This way, you'll have an array ofFormGroup
. In each of them, there will be aFormControl
with a unique name, and so your problems will disappear.This means you've got to make a
FormGroup
for each product in theproducts
array, and maybe a differentFormGroup
for the common controls (total, Subtotal...).Of course, this means that the
products
array in theFormGroup
no longer is necessary.Another advice is to remove data binding from the 'total' fields, as you won't edit them. You can change [(ngModel)] to just [ngModel] for them.
In fact, though I'm not an expert in reactive forms, I think that, if you're using FormControl and FormGroup, you don't need
ngModel
at all.