Search code examples
javascriptangularrxjsreactive-programming

Angular Rxjs: How to handle multiple events that affects a base observable stream


In Angular tutorial Get data from server (https://angular.io/tutorial/toh-pt6), there is a code snippet which handles the search, and making a http call to get the heroes with the search term.

import { Component, OnInit } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';
import { Hero } from '../hero';
import { HeroService } from '../hero.service';

@Component({
  selector: 'app-hero-search',
  templateUrl: './hero-search.component.html',
  styleUrls: [ './hero-search.component.css' ]
})
export class HeroSearchComponent implements OnInit {
  heroes$!: Observable<Hero[]>;
  private searchTerms = new Subject<string>();

  constructor(private heroService: HeroService) {}

  // Push a search term into the observable stream.
  search(term: string): void {
    this.searchTerms.next(term);
  }

  ngOnInit(): void {
    this.heroes$ = this.searchTerms.pipe(
      // wait 300ms after each keystroke before considering the term
      debounceTime(300),

      // ignore new term if same as previous term
      distinctUntilChanged(),

      // switch to new search observable each time the term changes
      switchMap((term: string) => this.heroService.searchHeroes(term)),
    );
  }
}

Let say I add another button to sort heroes by name, and it is handled by sort method

sort(order: 'asc' | 'desc') {
  this.sortingOrder$.next(order);
}

Take two different cases

Case 1: When the sort is click, fetch the heroes with both current searchTerm and sortOrder this.heroService.searchHeroes(term, sortOrder) and apply it to heroes$ stream.

Case 2: When the sort is click, empty the searchTerm and only fetch heroes by sortOrder e.g. this.heroService.searchHeroes('', sortOrder) and apply it to heroes$ stream. Meaning in this case the sort button and the search work independently.

How should I implement to update the heroes$ stream for those cases?


Solution

  • You can achieve this by using the combineLatest operator and adding the two observables to create your stream. It takes in an array of observable sources and once all sources have emitted at least once, an array of the mapped values are available for consumption. Please note that combineLatest will only emit once both sources have emitted at least one value. A BehaviorSubject is often helpful here since it emits a value when created as apposed to a Subject.

    See this StackBlitz demo I prepared