angularrxjspipeobservableconcatmap

Angular RXJS Sequential multiple post request


I tried to do following function with concat map. but i'm not sure what is the correct way for that.

//Req 01
await this.shippingInfoService.getShipmentInfoByCardId(this.data.CardId).then(async (shipmentData) => {
      if (shipmentData != undefined) {
        if (shipmentData.ShipmentId !== null && shipmentData.ShipmentId !== "") {
          //If shipment is assigned
          let shipmentId = shipmentData.ShipmentId;
          let contract = JSON.parse(sessionStorage.getItem(SITE_DETAILS_KEY)).Contract;

          let printObj = {
            "ShipmentId": Number(shipmentId),
            "Contract": contract
          }

//Second Req
          await this.orderDetailsService.printBillOfLading(printObj).then(async () => {
            await this.orderDetailsService.getBillOfLeadingPdfInfo(shipmentId).then(async (response) => {
              if (response.ResultKey === null || response.Id === null) {
                const dialogConfig = new MatDialogConfig();
                dialogConfig.data = "The information needed to generate a report isn't available.";
                dialogConfig.disableClose = true;
                this.dialog.open(ErrorDialogComponent, dialogConfig);
                //Hide Loading indicator
                this.store.dispatch(setLoadingSpinner({showLoading: false}));
              } else {
// 3rd Req
                let url = await this.orderDetailsService.generateBillOfLadingPdfUrl(response.ResultKey, response.Id);
                await window.open(url.url, "_blank");
                //Hide Loading indicator
                await this.store.dispatch(setLoadingSpinner({showLoading: false}));
              }
            });
          });
        } else {
          //If shipment is not assigned
          //Hide Loading indicator
          this.store.dispatch(setLoadingSpinner({showLoading: false}));
          //Error
          const dialogConfig = new MatDialogConfig();
          dialogConfig.data = "Shipment needs to be connected";
          dialogConfig.disableClose = true;
          this.dialog.open(ErrorDialogComponent, dialogConfig);
        }
      }
    })
  1. Request 02 is depend on Request 01
  2. Request 03 is depend on Request 02

Need to refactor above code with rxjs operators.


Solution

  • This is a refactoring of your code, altering as little as possible (to a point). I don't think this is the most idiomatic way to write RxJS, but then I also wouldn't write Promises as above either.

    I can't test any of this, so this is the shape of an answer, and not an answer unto itself. Maybe it'll get you started...

    Best of luck:

    //Req 01
    from(this.shippingInfoService.getShipmentInfoByCardId(this.data.CardId)).pipe(
      filter(shipmentData => shipmentData != undefined),
      tap(shipmentData => {
        if (shipmentData.ShipmentId !== null && shipmentData.ShipmentId !== "") {
          throw "Shipment needs to be connected"
        }
      }),
      // Second Req because shipment is assigned
      concatMap(shipmentData => this.orderDetailsService.printBillOfLading({
          "ShipmentId": Number(shipmentData.ShipmentId),
          "Contract": JSON.parse(sessionStorage.getItem(SITE_DETAILS_KEY)).Contract
        }).pipe(
          map(_ => Number(shipmentData.ShipmentId))
        )
      ),
      concatMap(id => this.orderDetailsService.getBillOfLeadingPdfInfo(id)),
      tap(response => {
        if ( response.ResultKey === null || response.Id === null) {
          throw "The information needed to generate a report isn't available."
        }
      }),
      // 3rd Req
      concatMap(response =>
        this.orderDetailsService.generateBillOfLadingPdfUrl(response.ResultKey, response.Id)
      ),
      // Catch errors thrown above
      catchError(error => {
        // This isn't really comprehensive error management for obvious reasons. 
        // I'm assuming the only errors are one of the two strings thrown above.
        const dialogConfig = new MatDialogConfig();
        dialogConfig.data = error;
        dialogConfig.disableClose = true;
        this.dialog.open(ErrorDialogComponent, dialogConfig);
    
        // Returning empty means we don't propogate this error
        return EMPTY;
      }),
      // Always finish by Hiding Loading indicator
      finalize(() => this.store.dispatch(setLoadingSpinner({showLoading: false})))
    ).subscribe(
      url => window.open(url.url, "_blank")
    );