Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
855 views
in Technique[技术] by (71.8m points)

rxjs - how can you create another observable from an observable but ignore its output in the parent observable?

I have a situation where I have an observable, and for each emitted item, I want to create another observable, but ignore that observable's value and instead return the result of the first observable.
For example, if I click a button, I want to track something that happens in another button, only when the first button is toggled on.

I can do this now, sort of, with a hack, by taking the output of the child observable and piping it to a mapTo with the parent's value. You can see it in this code, which can be played with in a code sandbox:

import { fromEvent, from } from "rxjs";
import { mapTo, switchMap, tap, scan  } from "rxjs/operators";

const buttonA = document.getElementById("a");
const buttonB = document.getElementById("b");

const textA = document.querySelector('#texta');
const textB = document.querySelector('#textb');

fromEvent(buttonA, 'click').pipe(
  // this toggles active or not.
  scan((active) => !active, false),
  switchMap(active => {

    if (active) {
      const buttonBClicks$ = fromEvent(buttonB, 'click');
 
      // here we can observe button b clicks, when button a is toggled on.
      return buttonBClicks$.pipe(
        // count the sum of button b clicks since button a was toggled on.
        scan((count) => count+1, 0),
        tap(buttonBCount  => {
          textB.value = `button b count ${buttonBCount}`;
        }),
        // ignore the value of the button b count for the final observable output. 
        mapTo(active)
      )
    } else {
      textB.value = ``;

      return from([active]);
    }
   
  })
).subscribe({
  next: buttonActive => {
    textA.value = `Button a active: ${buttonActive}`
  }
});

A couple issues here. In the case that the button is toggled on, the outer observable only receives a value once the button is clicked. This mapTo use seems hacky.

Any better ways to do this?

question from:https://stackoverflow.com/questions/66050842/rxjs-how-can-you-create-another-observable-from-an-observable-but-ignore-its-o

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

It sounds like you don't want the inner observable to actually be a part of the process at all. Are you waiting on it or anything?

If not, you can just do it all as a side effect as follows:

fromEvent(buttonA, 'click').pipe(
  scan((active) => !active, false),
  tap(active => { if(active) {
    fromEvent(buttonB, 'click').pipe(
      scan(count => count+1, 0),
      tap(buttonBCount  => {
        textB.value = `button b count ${buttonBCount}`;
      })
    ).subscribe()   
  }})
).subscribe({
  next: buttonActive => {
    textA.value = `Button a active: ${buttonActive}`
  }
});

Nested subscriptions are considered bad voodoo, so you ca refactor like this to keep your separation of conserns more apparent:

const trackActiveFromButton$ = fromEvent(buttonA, 'click').pipe(
  scan((active) => !active, false),
  shareReplay(1)
);

trackActiveFromButton$.subscribe({
  next: buttonActive => {
    textA.value = `Button a active: ${buttonActive}`
  }
});

trackActiveFromButton$.pipe(
  switchMap(active => active ?
    fromEvent(buttonB, 'click').pipe(
      scan(count => count+1, 0),
      tap(buttonBCount  => {
        textB.value = `button b count ${buttonBCount}`;
      })
    ) :
    EMPTY
  )
).subscribe();

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...