Reputation: 128
I am trying to capture hover events on a list of items between the first and second clicks of any of items in the list.
E.g.
I have setup an observable stream as follows:
let items = document.getElementsByClassName("item");
let itemClicks = Rx.Observable.fromEvent(items, "click");
let itemHover = Rx.Observable.fromEvent(items, "mouseenter");
let clicksWithFlag = itemClicks.scan((acc, val) => ({ val: val.target.innerHTML, firstClick: !acc.firstClick}), {});
//create stream of 1st clicks
let firstClicks = clicksWithFlag.filter(x => x.firstClick).pluck('val');
//create stream of second clicks
let secondClicks = clicksWithFlag.filter(x => !x.firstClick).pluck('val');
let hoverBetweenFirstAndSecondClick = firstClicks.flatMap(x => itemHover.takeUntil(secondClicks));
The problem I am running into is the itemHover.takeUntil(secondClicks)
doesn't end the itemHover stream. I think it's related to firstClicks and secondClicks stemming from the same observable, but can't figure it out.
It works if I swap the takeUntil(secondClicks) for any other stream (e.g. a stop button).
See this JSBin for an example: https://jsbin.com/dotapis/edit?js,console,output
Hope somebody can help. I am trying to build a range selector using RxJS, but am starting to think that splitting the stream into first and second clicks may not be the best route.
Just tried this with BaconJS and I can get it working with pretty much the exact same code. https://jsbin.com/fobumu/edit?js,console,output
I'm convinced the problem lies in non-idiomatic use of streams, but not sure why.
Upvotes: 2
Views: 1274
Reputation: 406
It seems you might be trying to force an imperative approach into a functional programming library, that's why it's becoming so complicated to make it work. This might be simpler:
let items = document.getElementsByClassName("item");
let button = document.getElementById("button1");
let itemClicks = Rx.Observable.fromEvent(items, 'click');
let itemHover = Rx.Observable.fromEvent(items, 'mouseenter');
let buttonClicks = Rx.Observable.fromEvent(button, 'click');
let clicksWithFlag = itemClicks.scan((acc, val) => (
{ val: val.target.innerHTML, firstClick: !acc.firstClick}
), {});
clicksWithFlag
.combineLatest(itemHover, (click, hover) => ({click, hover}))
.filter(arg => arg.click.firstClick)
.map(arg => arg.hover.target.innerHTML)
.subscribe(hover => console.warn('btn: hover: ' + hover));
As a general advise when working with Rx, try to find declarative solutions. If you start thinking how you transform a stream in order to get what you need instead of the steps to make that, things become easier.
Hope it helps, good luck!
Upvotes: 0