7

I'm using Angular 2 to make a directive. I have the following events bound to the host component:

host: {
    '(mouseenter)': 'onMouseEnter($event)',
    '(mouseleave)': 'onMouseLeave($event)'
}

I also created two streams and listeners on the directive to manage the two events

export class PopupDirective {
    private _mouseEnterStream: EventEmitter<any> = new EventEmitter();
    private _mouseLeaveStream: EventEmitter<any> = new EventEmitter();

    onMouseEnter($event) {
         this._mouseEnterStream.emit($event);
    }

    onMouseLeave($event) {
         this._mouseLeaveStream.emit($event);
    }
}

I want my subscribe to only be called if the mouseenter event is still active after a fixed delay (i.e., a mouseleave hasn't occured). I tried doing it this way, but it doesn't work (which makes sense, I just don't know how to fix it).

this._mouseEnterStream.flatMap((e) => {
  return Observable
    .of(e)
    .takeUntil(this._mouseLeaveStream);
}).delay(2000).subscribe(
  () => console.log('yay, it worked!')
);

Does anyone with Angular 2 / RxJS experience know how I should approach this?

Jack Guy
  • 8,346
  • 8
  • 55
  • 86
  • Where does `this._mouseStream` come from? – Günter Zöchbauer Feb 10 '16 at 05:21
  • @GünterZöchbauer Sorry that should be mouseEnterStream – Jack Guy Feb 10 '16 at 05:27
  • btw, since you're using Rx, you really don't need EventEmitters. Just use Subjects instead. Kind of surprised you can use EventEmitters as Observables, maybe ng2 converts them under the hood. – kakigoori Feb 10 '16 at 07:32
  • @kakigoori Isn't the whole point that EventEmitters *are* Observables? Thanks for the tip though. – Jack Guy Feb 10 '16 at 14:40
  • typically to use EventEmitters for Observables in other projects, you have to use the static `fromEvent` method to generate an observable from an event emitter. i used to be a heavy user of event emitters, but cold/hot streams, operators, and the whole creation/subscription/disposal is just too nice. (you might find Andre's post here useful: http://stackoverflow.com/questions/25338930/reactive-programming-rxjs-vs-eventemitter-in-node-js/35239241#35239241) – kakigoori Feb 10 '16 at 14:48
  • @kakigoori That was helpful thank you. It looks like ng2's EventEmitter might be a misnomer then. [Looking at the docs](https://angular.io/docs/ts/latest/api/core/EventEmitter-class.html) it seems very intentional that EventEmitter is based on Observer. – Jack Guy Feb 10 '16 at 15:31

2 Answers2

5

The Günter's answer is exactly what you expect but you should use the of operator instead of the return one that doesn't exist.

this._mouseEnterStream.flatMap((e) => {
  console.log('_mouseEnterStream.flatMap');
  return Observable
      .of(e)
      .delay(2000)
      .takeUntil(this._mouseLeaveStream);
}).subscribe(
  (e) => {
    console.log('yay, it worked!');
    console.log(e);
  }
);

See the corresponding plunkr: https://plnkr.co/edit/vP3xRDXxFanqzLEKd3eo?p=preview.

Also, there is a proposal in Angular that aims to simplify the way observables are created from DOM events using Rx via template syntax.

eAbi
  • 3,220
  • 4
  • 25
  • 39
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
  • If it's just a single value you want to return, `.just` makes more sense. But to non-Haskelly-lang users, it might be gibberish. – kakigoori Feb 10 '16 at 14:50
  • 1
    Thanks for your comment but this operator doesn't exist with Rxjs 5.0.0-beta.0. See this error: `Rx_1.Observable.just is not a function` – Thierry Templier Feb 10 '16 at 15:07
  • Oh, sorry, looks like it is listed in the migration docs for RxJS5 beta. Kind of sad though. – kakigoori Feb 10 '16 at 15:16
  • Not sure why `.subscribe` needed to be connected to `.flatMap` for this to work, but this is perfect! Thank you. – Jack Guy Feb 10 '16 at 15:17
  • It's just to create the data stream. When the first event is received, it will be mapped to another chain that handles the delay... – Thierry Templier Feb 10 '16 at 15:55
3

Looks quite similar to How do I timeout an event in RxJS?

this.myStream = this._mouseEnterStream
    .flatMap((e) => {
        return Observable
            .of(e)
            .delay(2000)
            .takeUntil(mouseLeaveStream);
    });

myStream.subscribe((x) => { 
        console.log('onNext: ', x);
});

I don't use TS or Rx myself (only Dart) therefore I don't know if this is the correct syntax or if the name of the operators match with these available for Angular.

Community
  • 1
  • 1
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 1
    Interestingly I tried this option exactly before making this post - but for some reason .susbcribe needs to be connected to .flatMap in order to make it work - otherwise "next" fires immediately on a mouseenter event. – Jack Guy Feb 10 '16 at 15:16