'How to do something conditionally with Observables before subscribe?

I'm trying to implement a solution that execute the following steps:

  1. Check if a condition is valid with a filter.
  2. Check if Ctrl is pressed and execute some function if it is.
  3. finally, execute another function.

I was able to make it work with that code:

const click = Observable.fromEvent(element, 'click');
click.filter(() => condition)
     .do((event: MouseEvent) => {
         if (!event.ctrlKey) {
             // maybe do something...
         }
     }).subscribe(() => {
         // aways do something else at end
     });

I want to know if there is a more elegant solution that removes the if condition inside of do method?



Solution 1:[1]

Not sure I understand the question, but here's a similar way of doing that and trying to keep the code as clean as possible :

const click$ = Observable.fromEvent(document, 'click');

// I don't know what's in your condition so I just return true
const condition = () => true;

const fnCtrlIsPressed = () => 'Ctrl is pressed';
const fnCtrlIsNotPressed = () => 'Ctrl is not pressed';

click$
  .map(event => event.ctrlKey)
  .map(isCtrlPressed => isCtrlPressed ?
    fnCtrlIsPressed():
    fnCtrlIsNotPressed())
  .do(console.log)
  .subscribe();

The output will be something like that :
enter image description here

Here's a working Plunkr : https://plnkr.co/edit/VwuXkk0QnVGC4hPCvtOo?p=preview

Solution 2:[2]

One option is to flatMap with a stream that triggers the appropriate action in case Ctrl is pressed, it is not much different from your approach.

const condition = () => true;

const clickStreamWithCtrlAction = Observable.fromEvent(element, "click")
  .flatMap(event => event.ctrlKey
    ? Observable.of(event).do(clickWithCtrl => console.log("With ctrl clause"))
    : Observable.of(event)
  )

clickStreamWithCtrlAction
    .filter(condition)
    .subscribe(event => console.log(`Finally clause, ctrlKey: ${event.ctrlKey}`));

Solution 3:[3]

why you don't use pipe method. that is something like this:

import { Observable, tap, filter } from 'rxjs';
const click = Observable.fromEvent(element, 'click')
                   .pipe(filter((valueEmitted) => {
                       //your filter comes here. if everything ok return true;
                   }), tap((valueEmitted) => {
                       //do something before subscribe
                   }));

and after that you can subscribe to click observable.

more details about tap: https://www.learnrxjs.io/learn-rxjs/operators/utility/do

more details about filter: https://www.learnrxjs.io/learn-rxjs/operators/filtering/filter

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 maxime1992
Solution 2 Tal
Solution 3 Amir Hesari