zardosht
zardosht

Reputation: 11

Call Angular service inside forEach loop

I am beginner in Angular 4. I want to calculate total invoice in EUR curency, I have value of prices and currenciy type for each price. When I want to calculate total sum, I need to call exchangeService service , in order to get exchange rate of currency.

When I inspect the value of invoicesSumTotalValue it is 0. Here is my code:

    calcInvTotalValue(){

     let prods = (<FormArray>(<FormGroup>(<FormArray>this.productData.parent.parent).parent).controls['products']);
        let invoicesSumTotalValue = 0;


        prods.controls.forEach(c => {
          let price = (<FormGroup>(<FormGroup>c).controls['selected_products']).controls['price'].value;
          let currency = (<FormGroup>(<FormGroup>c).controls['selected_products']).controls['currency'].value;

          this.exchangeService.getExchRate(currency, "USD").subscribe(
            vl => {
              let exRate = vl;
              invoicesSumTotalValue = invoicesSumTotalValue + (parseFloat(price) * exRate);
            },
            (error: any) => console.log(error)
          );
        });
        console.log("iNVOICE total is: " + invoicesSumTotalValue);

How can I call service for each value of prods array ? Thank you!

Upvotes: 0

Views: 2857

Answers (2)

wentjun
wentjun

Reputation: 42536

You should make use of RxJS's forkJoin to wait for the Array.forEach() loop to be completed before returning all the observables. If you are familiar with the usage of Promises in JavaScript, it is actually similar to Promise.all.

You will face issues calculating invoicesSumTotalValue because the returning of observables is an asynchronous operation, hence it is likely the next iteration of the forEach loop will be executed before the response from getExchRate() is returned.

Here is my suggested modifications to your code. Basically, we make use of forkJoin() to return the observables once all iterations of the forEach() loop have been completed. From there, within the subscribe() block where we return the observables, we handle the calculation of invoicesSumTotalValue.

const prods = (<FormArray>(<FormGroup>(<FormArray>this.productData.parent.parent).parent).controls['products']);
const observablesList = [];

prods.controls.forEach(c => {
  const currency = (<FormGroup>(<FormGroup>c).controls['selected_products']).controls['currency'].value;
   observablesList.push(this.exchangeService.getExchRate(currency, 'USD'));
})

forkJoin(observablesList).subscribe(response => {
  //console.log(response);
  // handle the rest
});

Upvotes: 1

Karthikeyan
Karthikeyan

Reputation: 87

You are trying to console.log an asynchronous value "invoicesSumTotalValue" synchronously that's the issue here.

Move the method level variable invoicesSumTotalValue to class level and print the value in HTML template you will get the actual total value.

Since the http service returns an observable of price values it won't be immediately available where you are doing the console.log.

Modify the code like below and it will work fine : ( I mocked the prod array for my convenience to show the result here )

Component Code :

export class ExchangeComponent implements OnInit {

invoicesSumTotalValue = 0;

constructor(private exchangeService: ExchangeService) { }

ngOnInit() {
    this.calcInvTotalValue();
}

calcInvTotalValue() {

    let prods = [
        {
            price: 10,
            currency: 'INR'
        },
        {
            price: 20,
            currency: 'EUR'
        }
    ];

    prods.forEach(c => {
        let price = c['price'];
        let currency = c['currency'];

        this.exchangeService.getExchangeRate("USD", currency).subscribe(
            vl => {
                let exRate = vl['rates'][currency];
                this.invoicesSumTotalValue = this.invoicesSumTotalValue + (parseFloat(String(price)) * parseFloat(exRate));
            },
            (error: any) => console.log(error)
        );
    });
}

}

Template Code :

{{ invoicesSumTotalValue }}

Upvotes: -1

Related Questions