30

[angular 2.4.5]

I tried and it seems to work like an EventEmitter:

  • My component from outside:

    <split (visibleTransitionEnd)="log($event)"></split>
    
  • Inside the component:

    @Output() visibleTransitionEnd: Observable<string>
    observer: Observer;
    
    constructor() {
      const myObs = new Observable(observer => this.observer = observer);
    
      this.visibleTransitionEnd = myObs
        .map(x => '> ' + x + ' <')
        .debounceTime(20)
        .do(() => console.log('here we are!'));
    }
    
  • Then I can call inside component:

    // needed condition because if nobody subscribe 'visibleTransitionEnd' > no observer!
    if(this.observer) this.observer.next('test');
    

View this plunker

I like this because there's no subscription inside my component.

But is it a bad way to achieve this? What's the risk/wrong?

Is it better to use a Subject?

Liam
  • 27,717
  • 28
  • 128
  • 190
bertrandg
  • 3,147
  • 2
  • 27
  • 35
  • 1
    Possible duplicate of [Delegation: EventEmitter or Observable in Angular](https://stackoverflow.com/questions/34376854/delegation-eventemitter-or-observable-in-angular) – Evan Carroll Feb 17 '19 at 10:06

4 Answers4

18

EventEmitter just extends Subject, so this is no surprise (and I also saw this already in Dart).

They use their own class to be able to alter the implementation later without breaking existing code.

So it might not be the best idea to circumvent this abstraction. If you are aware of the disadvantage and willing to accept it, you can of course do it.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
10

Well, in your situation you could use EventEmitter or Subject. You can see that an EventEmitter is just like Subject (although it's recommended to use EventEmitter if you can). https://github.com/angular/angular/blob/master/modules/%40angular/facade/src/async.ts

The Observable.create (or new Observable()) is not intended to be used like this. The inner function should emit values to the observer and return a tear down function (to release resources or whatever). Not to be kept as a property.
However, I'm not sure what consequences it might have (memory leaks?).

So rather use Subject instead:

export class SplitComponent implements OnDestroy {
  @Output() visibleTransitionEnd: Observable<string>
  visibleTransitionEndObserver: Subject;

  constructor() {
    const subject = new Subject();

    this.visibleTransitionEnd = subject.asObservable()
      .map(x => '> ' + x + ' <')
      .debounceTime(20)
      .do(() => console.log('here we are!'));
  }

  // ...
}
Liam
  • 27,717
  • 28
  • 128
  • 190
martin
  • 93,354
  • 25
  • 191
  • 226
  • thx, I've updated my plunker following answers and it works well with a `Subject` without adding `asObservable` operator, why did you add it? – bertrandg Feb 08 '17 at 17:22
  • @bertrandg See http://stackoverflow.com/questions/39703167/changes-in-observable-not-reflected-in-view/39721493#39721493 and https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/asobservable.md – martin Feb 08 '17 at 17:24
2

2 reasons to choose EventEmitter

  1. Angular EventEmitter can make sure to delivery event asynchronously if needed. It's good for responsive user experience.
  2. Encapsulate the underline implementation. If someday, next version of Angular would update event binding which depends on something new of EventEmitter. It would be disaster of your project if Subject extensively used. Not sure about this. But should be avoided.

Angular EventEmitter extends RxJS Subject with only 3 methods so far: 1) constructor(), 2) subscribe(next, error, complete) and 3) new method emit(value) {super.next(value);}

If you new EventEmitter(true), it will deliver events asynchronously

    constructor(isAsync = false) {
        super();
        this.__isAsync = isAsync;
    }

EventEmitter.subscribe() does something to async delivery event according to this._isAsync.

xuemind
  • 405
  • 3
  • 11
2

I want to point out an advantage of using Observable for output interface is you can basically throw in any observable you want

So let say if you if you have an event that will only fire when the form is valid and pass on the value.

With event emitter you'll have to define extra Emitter instance to handle that

@Output validValue = new EventEmitter()
this.form.valueChanges.pipe(tap(value=>{
if(ths.form.valid) 
   validValue.emit(value)
})).subscribbe()

Using Observable as event emission you can simply do this

@Output() validValue=this.form.valueChanges.pipe(filter(_=>this.form.valid))
Fan Cheung
  • 10,745
  • 3
  • 17
  • 39