1

I have a form input for a search. When a user enters something in the search input I need to call 2 separate api's. This is what I'm trying to achieve:

myInput: new FormControl();
listOne: Observable<string[]>;
listTwo: Observable<string[]>;

ngOnInit(){
    this.listOne = this.myInput.valueChanges
                     .debounceTime(500)
                     .distinctUntilChanged()
                     .switchMap(myInput => this._service.getListOne(myInput));

    this.listTwo = this.myInput.valueChanges
                     .debounceTime(500)
                     .distinctUntilChanged()
                     .switchMap(myInput => this._service.getListTwo(myInput));
}

So my question is, how do I subscribe to valueChanges and call 2 different api's to fill 2 different arrays? With the code above, it works great on the initial search, but when I change the search text, only getListTwo is called.

user3743222
  • 18,345
  • 5
  • 69
  • 75
garethb
  • 3,951
  • 6
  • 32
  • 52

2 Answers2

3

Whenever a source has several subscribers, and you want those subscribers to read the same value from that source (in other words you want to multicast the source values to several subscribers), you should share.

Here that means : sharedSource = this.myInput.valueChanges.debounceTime(500).distinctUntilChanged().share(), so for example :

myInput: new FormControl();
listOne: Observable<string[]>;
listTwo: Observable<string[]>;

ngOnInit(){
    this.listOne = sharedSource
                     .switchMap(myInput => this._service.getListOne(myInput));

    this.listTwo = sharedSource
                     .switchMap(myInput => this._service.getListTwo(myInput));
}

You can have a look at the detailed explanation here of multicast vs. cold streams.

Community
  • 1
  • 1
user3743222
  • 18,345
  • 5
  • 69
  • 75
  • How would I unsubscribe from the observable? Do I need to unsubscribe from sharedSource or listOne/listTwo? Since I don't call subscribe it looks like unsubscribe is not available. – garethb Sep 19 '16 at 11:03
  • Well, subscribe is called somewhere, so if it is not you, it is the framework, which means the framework also call unsubscribe. When that unsubscribe is called, the upper streams will also be unsubscribed. So if that's the case you don't have to do anything. – user3743222 Sep 19 '16 at 22:47
  • You are correct, using Angular2 and the async pipe in ng-for takes an observable and subscribes/unsubscribes in the framework. eg ng-for="let x of listOne | async" <= this subscribes/unsubscribes – garethb Sep 21 '16 at 05:08
1
ngOnInit(){
  input$ = this.myInput.valueChanges.debounceTime(500)
                         .distinctUntilChanged()
                         .share();

  this.listOne = input$.switchMap(myInput => this._service.getListOne(myInput));
  this.listTwo = input$.switchMap(myInput => this._service.getListTwo(myInput));

  //store subscriptions
  this.subs$.push(input$,this.listOne,this.listTwo);
}

Remember to unsubscribe to avoid memory leaks:

ngOnDestroy(){
    this.subs$.forEach(s => s.unsubscribe());
}
BeetleJuice
  • 39,516
  • 19
  • 105
  • 165
  • I take it I should declare subs$ as Array? And why store the subscriptions? Is that only used to unsubscribe? – garethb Sep 07 '16 at 02:24
  • @garethb `Array>`. I store them so I can unsubscribe and avoid memory leaks. You would also want to store a subscription if you need to pass it around, for instance to another component or service. – BeetleJuice Sep 07 '16 at 03:27
  • I had to declare as Array but all working now. +1 for reminder to unsubscribe! – garethb Sep 08 '16 at 00:15