Search code examples
angularrxjs6chain

Angular 7 / Rxjs: chaining and nesting observables


I have a data set of transactions, that I would like to enrich with additional data like the exchange rate at the given date and post it straight back to my api. But I only get my original transactions without any modifications and I'm still new to Angular and Rxjs. So I could use some help with the operators.

I have a button that calls several apis from a function:

// component.ts
public click() {
    this.deposits = this.depositApi.getAllDeposits()
      .subscribe(
        result => {
          result.map(x => this.depositApi.enrichAndSaveDeposit(x));
        }
      );
  }

Get all raw transactions from local api which all have dates. (This works)

// depositApiService
public getAllDeposits(): Observable<DepositModel[]> {
    return this.http.get<DepositModel[]>(AppConfig.localJsonServerUrl + AppConfig.api.deposits)
      .pipe(
        catchError(this.handleError('getAllDeposits', null))
      );
  }

Here I call an external api to get the exchange rate for a given date and then make some calculations and post it back to local api.

But it never gets into the mergeMap part.

// depositApiService
public enrichAndSaveDeposit(deposit: DepositModel): Observable<DepositModel> {
    return this.apiService.getHistoricEurRate(deposit.date)
      .pipe(
        mergeMap(data => {
          deposit.historicExchangeRate = data.rates.USD;
          deposit.exchangeRate = deposit.sellAmount / deposit.buyAmount;
          deposit.sellAmountInUsd = deposit.sellAmount * data.rates.USD;
          deposit.exchangeRateInUsd = deposit.exchangeRate * data.rates.USD;
          return this.saveLocalDeposit(deposit);
        }), catchError(this.handleError('enrichAndSaveLocalDeposit', deposit))
      );
  }

Here the external api is called (this works).

// apiService
public getRemoteExchangeRates(): Observable<ExchangeRateModel> {
    return this.http.get<ExchangeRateModel>(AppConfig.exchangeRateApi + '/latest')
      .pipe(
        catchError(this.handleError('getRemoteExchangeRates', null))
      );
  }

This is the post to local api. (Never gets to this point)

// depositApiService
private saveLocalDeposit(deposit: DepositModel): Observable<DepositModel> {
    return this.http.post<DepositModel>
      (
        AppConfig.localJsonServerUrl + AppConfig.api.deposits,
        deposit,
        { headers: new HttpHeaders().set('Accept', 'application/json') }
      )
      .pipe(
        catchError(this.handleError('saveLocalDeposit', deposit))
      );
  }

Solution

  • here is an answer I gave for a similar question that outlines how you can go about this how to build single object from 2 http requests in angular, without adding another value

    In addition, I want to provide to you a tip about the RxJS that I think is currently part of the issue. Let's look at your code as shown below.

    public click() {
        this.deposits = this.depositApi.getAllDeposits()
          .subscribe(
            result => {
              result.map(x => this.depositApi.enrichAndSaveDeposit(x));
            }
          );
      }
    

    This code subscribes to the Observable getAllDeposits, and then says when that returns with a value, map that value using enrichAndSaveDeposit. However, your code for enrichAndSaveDeposit is also an Observable, so as it is written above, that will never be called, since it is never subscribed to. Below I have written something that would fix this specific case.

    public click() {
        this.deposits = this.depositApi.getAllDeposits()
          .subscribe(
            result => {
              result.map(x => {
                this.depositApi.enrichAndSaveDeposit(x)
                  .subscribe( // this is the subscribe that is needed to make the second part of the chain work
                    enrichedResult => {
                      // do something with the enrichedResult here
                    }
                  );
              };
            }
          );
      }
    

    Hope this helps.