-1

i have observbale made from array of form controls in angular.i am using dropdowns and inputs to sum up all the currencies in relation to one currency and it works but my issue is that when i want to update the field itself, value of sum is still not changed because of http request delay. i want to set value of field when all the code is finished executing and sum has its final form.

 let sum = 0;
 let arrayOfControls = [];

    for (let control of this.currencies.controls) {
      arrayOfControls.push(control);
    }

    const myObservable = from(arrayOfControls);

    const myObserver = {
      next: (control) => {
        let currency = control.get('dropdown').value;
        let amount = Number(control.get('amount').value);
        if (currency && amount) {
          this.updateRate(currency).subscribe((serverData) => {
            let rate = serverData['rates'][this.dropdown2.value];
            sum += amount * rate;
            // sum changes here.
          });
        }
      },
      complete: () => {
         // when i try to set value of field, sum has not changed yet due to delay.
        this.amount2.setValue(sum.toFixed(2));
      },
    };

    myObservable.subscribe(myObserver);
Vazhaabdu
  • 23
  • 3

2 Answers2

0

You'd need to avoid nested subscriptions and try to use higher order mapping operators like switchMap. See here for more info. I've also used RxJS NEVER constant to not emit when the condition isn't met.

Try the following

import { from, NEVER } from 'rxjs';
import { switchMap } from 'rxjs/operators';

let sum = 0;
let arrayOfControls = [];

for (let control of this.currencies.controls) {
  arrayOfControls.push(control);
}

const myObservable = from(arrayOfControls).pipe(
  switchMap(control => {
    let currency = control.get('dropdown').value;
    let amount = Number(control.get('amount').value);
    return (currency && amount) ? this.updateRate(currency) : NEVER;
  })
);

const myObserver = {
  next: (serverData) => {
    let rate = serverData['rates'][this.dropdown2.value];
    sum += amount * rate; // sum changes here
  },
  complete: () => {
    this.amount2.setValue(sum.toFixed(2));
  },
};

myObservable.subscribe(myObserver);
ruth
  • 29,535
  • 4
  • 30
  • 57
0

Here is how I would implement this:

I would make an array of updateRate calls and forkJoin them all together. When they all complete, I sum up the rates*amounts

That would look something like this:

const rateCurrencies = this.currencies.controls
  // Define currency and amount
  .map(control => ({
    currency: control.get('dropdown').value,
    amount: Number(control.get('amount').value)
  }))
  // Remove controls that are missing a currency or amount
  .filter(({currency, amount}) => currency && amount)
  // Define updateRate call and map into returned rate and current amount
  .map(({currency, amount}) => 
    this.updateRate(currency).pipe(
      map(serverData => ({
        rate: serverData['rates'][this.dropdown2.value],
        amount
      }))
    )
  );

let sum: number;
// run our array of updateRate calls concurrently, gets results as an array
forkJoin(rateCurrencies).subscribe(updates => 
  // sum up the array where each element is amount * rate
  sum = updates.reduce(
    (acc, curr) => acc + (curr.amount * curr.rate), 
    0
  )
);
Mrk Sef
  • 7,557
  • 1
  • 9
  • 21