3

I have my reactive form which looks something like this

<form [formGroup]="addInvoiceForm" (submit)="onAddInvoiceFormSubmit()">
  <div [formArrayName]="itemRows">
    <div *ngFor="let itemrow of addInvoiceForm.controls.itemRows.controls; let i=index" [formGroupName]="i">
      <input autocomplete="off" type="text" formControlName="itemqty">
      <input autocomplete="off" type="text" formControlName="itemrate">

      <!-- the input field below is to be summed -->
      <input autocomplete="off" type="text" formControlName="itemamt" [ngModel]="itemrow.get('itemqty').value * itemrow.get('itemrate').value">

    </div>
  </div>
  <button type="button" (click)="addItemRow()">Add New Row</button>
</form>

I want to sum the itemamt of all the rows created by user and pass them into form data. Can someone help me, tell me how to sum all the itemamt fields in the form and show user side by side what's the total amount?

4 Answers 4

7

I do wonder if there is a better way, but I tried using a getter, which complained about Expression has changed... Trying to add ChangeDetectorRef to the mix caused an infinite loop.

So I settled for listening for valueChanges in the form array, where I also needed to use ChangeDetectorRef, so first import that and inject it in your constructor:

import { ChangeDetectorRef } from '@angular/core';

constructor(private fb: FormBuilder, private ref: ChangeDetectorRef) { }

Then the valueChanges:

// subscribe to changes in the formArray
this.invoiceForm.get('itemRows').valueChanges.subscribe(values => {
  // reset the total amount  
  this.summed = 0;
  const ctrl = <FormArray>this.invoiceForm.controls['itemRows'];
    // iterate each object in the form array
    ctrl.controls.forEach(x => {
      // get the itemmt value and need to parse the input to number
      let parsed = parseInt(x.get('itemamt').value)
      // add to total
      this.summed += parsed
      this.ref.detectChanges()
    });
  }) 

To the build of your form, you need to add a new form control, since you wanted it would be part of the form:

this.invoiceForm = this.fb.group({
  itemRows: this.fb.array([this.initItemRows()])
  summed: [null] // here
}); 

And in your template:

<input readonly [ngModel]="summed" formControlName="summed" />

Demo


Edit:

I suggest you use the solution that our good yurzui suggested, much more elegant than my solution, so inside valueChanges use reduce()

resolvedPromise.then(() => {
  this.summed = values.reduce((acc, cur) => acc + cur.itemamt, 0);
});

and the variable:

const resolvedPromise = Promise.resolve(null);
Sign up to request clarification or add additional context in comments.

5 Comments

I would use values to calculate sum plnkr.co/edit/Xm0LduSK7GEYCg3n3HV1?p=preview And you call detectChanges on every iteration
@yurzui very nice, knew there had to be "a better way" ;) Thanks, learned something new again today :)
@AJT_82 There's a little error somewhere in your code. The itemamt of second row is being added twice.
The plunkr you mentioned in the comments works very well.
Thanks @AJT_82 ;)
0

The summing logic would be best in the component class, not in the HTML. You could define a property with a getter that returned the sum.

In general, the syntax would be something like this:

get sum():number {
    let summed = 0;
    for (var key in sample) {
        summed += sample[key];
    };
    return summed; 
} 

NOTE: You could also use Array.prototype.reduce(), but the above syntax is more straightforward.

Also, consider using ngSubmit instead of submit.

2 Comments

Can you explain me with example of code please? Actually I'm very new to Angular and all that stuff
If you could put together a plunker with the basics of your code, we can provide more specific assistance.
0

if you want sum all values before submitting post request you can do like this.

In your submit request pass form value

<form [formGroup]="addInvoiceForm" novalidate (submit)="onAddInvoiceFormSubmit(addInvoiceForm.value)">

</form>

In the component file.

you will have updated form object. just iterate the addInvoiceForm.itemRows and create sum.

onAddInvoiceFormSubmit(addInvoiceForm){
// you will have update form object. just iterate the addInvoiceForm.itemRows and create sum.

}

Note:- do all validation before enabling submit Button

Comments

0

answer from yurzui suggestion

import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms';

const resolvedPromise = Promise.resolve(null);

@Component({
  selector: 'my-app',
  template: ...
})


export class App implements OnInit {
    public invoiceForm: FormGroup;
    summed: number;

    constructor(private fb: FormBuilder, private ref: ChangeDetectorRef) {}
    
    ngOnInit(){
        this.invoiceForm = this.fb.group({
          itemRows: this.fb.array([this.initItemRows(), this.initItemRows()])
        });
        
        this.invoiceForm.get('itemRows').valueChanges.subscribe(values => {
          resolvedPromise.then(() => {
            this.summed = values.reduce((acc, cur) => acc + cur.itemamt, 0);
          });
        })
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.