20

I have a slider component that emits numbers while you drag the slider, which is usable like so: <my-slider (change)="onSlide($event)"></my-slider>. I'd like to get rid of the onSlide method and instead bind the change event stream to an Observable<number> property (instead of a callback).

I am using an Angular 2 EventEmitter for my @Output. EventEmitter inherits from a RxJs Subject, which is an Observable and an Observer. I wish to reuse the Observable side of the EventEmitter.

Of course I can push values that enter through onSlide onto another Subject, but I wish to prevent this boilerplate and overhead. Is there a way?

Lodewijk Bogaards
  • 19,777
  • 3
  • 28
  • 52
  • 6
    You can do `(change)="myOutput.emit($event)"`. Assuming that I understood you correctly. – Eric Martinez Dec 31 '15 at 16:59
  • That would save me the boilerplate of the callback (great). I would still have to copy from one subject to the next. – Lodewijk Bogaards Dec 31 '15 at 17:10
  • You could put your EventEmitter in a Service, have your component call an API/method on the service to trigger/`emit()` the event, and all subscribers will be notified. So no copying. Here's [an example](http://stackoverflow.com/questions/34376854/delegation-eventemitter-in-angular2/34402436#34402436). – Mark Rajcok Dec 31 '15 at 23:26
  • 1
    Nice idea, but this doesn't reduce boilerplate nor does it work with multiple components since services are singletons. Perhaps I could work something out, but that would break the angular api. I would prefer Eric's way, which doesn't cause much overhead anyway. – Lodewijk Bogaards Jan 01 '16 at 11:07
  • @LodewijkBogaards Did you find a better way to do this ? Without any boilerplate code or copy to different subject. – Adrian Fâciu May 03 '16 at 12:40
  • @AdrianFaciu nope, would have posted it here. – Lodewijk Bogaards May 03 '16 at 18:05
  • You could get your viewChildren of type MySlider and tie into the event output. It would be difficult to know which slider is for what external value. I guess you could pass an input identifier but that is getting ugly. – Jacob Roberts Sep 30 '16 at 19:49

2 Answers2

4

Not sure if relevant or if it helps someone: I ran into a similar issue: I wanted to consume a property of type Observable<number> provided by a component in the parent component.

After reading Observables and Reactive Programming in Angular 2, I noticed that I need to "turn" around my architecture and let the parent create the Observable and then assign it to the child.

So instead of having a MyComponent with an @Output() of type Observable<number> (which was then initialized to new BehaviorSubject(0) and modified using calls to next), I changed MyComponent to haven an @Input() of type BehaviorSubject<number>:

@Component({
  selector: 'my-component',
  template: `
    <button (click)="increment()">Click me!</button>
  `,
})
export class MyComponent {
  private _count : number = 0;

  @Input()
  public counter : BehaviorSubject<number>;

  public increment() {
    this._count++;

    if (this.counter)
      this.counter.next(this._count);
  }
}

The parent now creates a BehaviorSubject and binds to the property. It can easily consume the BehaviorSubject as an Observable:

@Component({
  selector: 'my-app',
  template: `
    <my-component [counter]="count"></my-component>
    <span>{{text | async}}</span>
  `,
})
export class App {
  public text : Observable<string>;

  public count : BehaviorSubject<number> = new BehaviorSubject(0);

  constructor() {
    this.text = this.count.map(n => "Click: " + n);
  }
}

Plunker: https://plnkr.co/edit/rKtVWmmGnFn4Po4B4CCL?p=preview

So in your case, I would say that your slider should provide an @Input (maybe call it value or number) and let the parent assign an observable (instead of letting the child create one).

Matthias
  • 12,053
  • 4
  • 49
  • 91
1

One of core difference between Angular 1 and Angular 2 is that change detection is always performed from top to bottom. That makes performance much better. Also there is no need to perform "stabilization" ($digest loop) as it was in Angular 1.

In Angular 2 you can't "push" change back in other way, except emitting event with EventEmitter/Subject, so Angular 2 can start checking component's Change Detectors.

Here is nice article about this topic.

Valentyn Shybanov
  • 19,331
  • 7
  • 66
  • 59