angularrxjsdialogmodal-dialog

Return only one of two observables that both have the same base stream depending on condition


I want to create a stream that is based of another stream and during processing of this base stream a condition should be checked. If the condition is true, then the base stream should switch over to a new observable stream. If the condition is not met, the value in the original base stream should be emitted.

It is basically an if-else check in the rxjs pipeline: Exmaple: I have an Angular CDK dialog and when the first dialog is confirmed, I want to check a condition and if this condition is true, a second dialog should open. If the second dialog is also confirmed, then the value should be emitted. If the second dialog is not confirmed, nothing should be emitted. If the condition check before opening the second dialog is false, the value in the base stream should be emitted.

I have a base stream which opens a dialog when it was triggered by another stream and it looks like this:

const firstModalConfirmed$ = this.clickSource$.pipe(
  filter((clickData) => this.isRequiredModalDataAvailable(clickData)),
  tap((clickData) => this.logger.debug("opening modal: ", clickData)),
  switchMap((clickData) =>
    this.openDetailModal(clickData).pipe(
      tap((modalResult) => this.logger.debug("Modal Result", modalResult)),
      filter((modalResult) => modalResult === "MODAL_CONFIRMED"),
      map((_modalResult) => clickData)
    )
  ),
  share()
);

This works so far.

Now I want to open a second dialog when openDetailModal() has been confirmed. But the second dialog should only be opened, when a condition for that is met. When it is not met, the stream should emit clickData from firstModalConfirmed$.

I tried to use iif() to determine which stream should be used. However it does not work and secondModalConfirmed$ is triggered before each clickSource$ from the base stream is emitted.

This is the code I came up with:

const firstModalConfirmed$ = this.clickSource$.pipe(
  filter((clickData) => this.isRequiredModalDataAvailable(clickData)),
  tap((clickData) => this.logger.debug("opening modal: ", clickData)),
  switchMap((clickData) =>
    this.openDetailModal(clickData).pipe(
      tap((modalResult) => this.logger.debug("Modal Result", modalResult)),
      filter((modalResult) => modalResult === "MODAL_CONFIRMED"),
      map((_modalResult) => clickData)
    )
  ),
  share()
);


// open second dialog and only continue when stream returns true
const secondModalConfirmed$ = this.openSecondModal().pipe(
  // only continue when "isConfirmed" is true
  filter((isConfirmed) => isConfirmed)
);

const stream$ = firstModalConfirmed$.pipe(
  switchMap((clickData) =>
    iif(
      () => {
        const { secondModalData } = this._vmSource.getValue();
        // check condition for second dialog
        return this.isScenarioForSecondModal(clickData, secondModalData);
      },
      // if condition is met, use secondModalConfirmed$
      secondModalConfirmed$.pipe(map((_) => clickedSeat)),
      // if condition is not met, use value from firstModalConfirmed$
      of(clickData)
    )
  )
);

Solution

  • If I understand the problem right, the key part of your question is

    I want to open a second dialog when openDetailModal() has been confirmed. But the second dialog should only be opened, when a condition for that is met. When it is not met, the stream should emit clickData from firstModalConfirmed$.

    If this understanding is right, then you could do something like this

    firstModalConfirmed$.pipe(
       // decide what to do when the first mode is confirmed
       switchMap( (clickData) => {
         // switchMap requires that an Observable is returned, but which Oservable
         // is the core of the question
         // If second condition is valid, then we switch to the second modal
         // If second condition is not valid, then return the value returned
         // by the first Modal using the "of" function from RxJs
         return isSecondConditionConfirmed() ? 
             secondModalConfirmed$.pipe(...) :
             of(clickData)
       })
    )