Observable seems to persist despite in pipe subscribed by async pipe

Viewed 81

In our application, we have a page that should only be navigated to if a certain state is set, let's call it requiredState$ on Page C.

A user has to chose some values on it's way to said page, by doing so he sets the requiredState$ on Page B. The user can start this selection cycle mutliple times, meaning that he will navigate multiple times to Page C. Starting point is Page A. Upon navigation from Page A to Page B, the state is resetted.

The order is Page A (reset states) -> Page B (select state) -> Page C (require state) -> Page A and so on.

To avoid navigation to Page C without without requiredState$ being set, we have following check in our pipe on Page C:

this.intermediateObservable$ = this.requiredState$.pipe(
  tap((requiredState) => {
    if (!requiredState) {
      // navigate back to Page B (*)
      this.cancelPipe$.next();
    }
  }),
  // ...
  takeUntil(this.cancelPipe$),
  // ...
);

whereas cancelPipe$ is a simple Subject.

The Observable this.intermediateObservable$ is used in three places

  1. To produce the displayed data

    this.data$ = this.intermediateObservable$.pipe(...)

    which then is subscribed by async pipe in the HTML template

  2. When the user selects a category

    this.intermediateObservable$.pipe(
      take(1)
    ).subscribe((someData) => {
      // filter out desired category and save to state service
    });
  1. When the user wants to display informational data
    this.modalContent$ = this.intermediateObservable$.pipe(
      // map information to show
    );

Again, this.modalContent$ is passed via async pipe to a modal

Problem

In the first cycle, everything is fine. The user sets the state on Page B, navigates to Page C to do some more things, and goes back to Page A. Now if he starts the second cycle, he navigates to Page B (which just before navigation, resets the required state). This seems to trigger the marked line (*) from Page C, resulting in a "double navigation" to Page B.

How is this even possible? I am aware that pipe() creates a new observable, meaning there are three of them.

  1. and 3. are not subscribed directly to, but later via async pipe, which should be handled autoatically.
  2. uses the take(1) operator, which in my understanding should directly unsubscribe afterwards.
1 Answers

The answer is quite simple: We did not call this.cancelPipe$.next() in successful situations, meaning that takeUntil() was not called and the pipe was still active.

Related