23

I have the following code inside my constructor:

this.searchResults = this.searchTerm.valueChanges
    .debounceTime(500)
    .distinctUntilChanged()
    .switchMap(term => this.apiService.search({
        limit: this.searchResultsLimit,
        term: term
    }));

And this is my input

<input type="text" [formControl]="searchTerm" />

You can see the tutorial I followed to get the code here.

My API service method is as followed:

searchCompanies(options): Observable<any[]> {
    return this.jsonp.get('api/search', this.formatOptions(options)).map(res => {   
        return res.json();
    });
}

Each time searchTerm is changed inside my input, the API call is fired. My problem is that the call is fired even when my input is empty (such as typing a query, then backspacing it all).

My question is, how can I only get my observable to fire when the value of `searchTerm is not empty/null?

martin
  • 93,354
  • 25
  • 191
  • 226
Fizzix
  • 23,679
  • 38
  • 110
  • 176

4 Answers4

38

Most easily just use the filter() operator to filter out all empty terms:

this.searchResults = this.searchTerm.valueChanges
    .filter(term => term) // or even better with `filter(Boolean)`
    .debounceTime(500)
    .distinctUntilChanged()
    .switchMap(term => this.apiService.search({
        limit: this.searchResultsLimit,
        term: term
    }));
martin
  • 93,354
  • 25
  • 191
  • 226
9

If you want to avoid the API call and want the search results to be reset when the search term is empty, test for an empty string in switchMap and return an empty observable in that situation:

this.searchResults = this.searchTerm
  .valueChanges
  .debounceTime(500)
  .distinctUntilChanged()
  .switchMap(term => term ?
    this.apiService.search({
      limit: this.searchResultsLimit,
      term: term
    }) :
    // If search term is empty, return an empty array
    // or whatever the API's response for no matches
    // would be:
    Observable.of([]) 
  });
cartant
  • 57,105
  • 17
  • 163
  • 197
  • Neat. But unfortunately, the `searchResults` does not get emptied when the `term` is empty/null. – Fizzix Feb 20 '17 at 01:12
  • 3
    Well, in that case you should return whatever the API would return if there was no match. For example, to return an empty array, replace `Observable.empty()` with `Observable.of([])`. – cartant Feb 20 '17 at 01:13
2

In Rxjs 6 so updated to use pipe you can stop the observable from processing so nothing is propagated downstream using EMPTY:

this.searchResults = this.searchTerm.valueChanges
    .pipe(
      debounceTime(500)
      distinctUntilChanged()
      switchMap(term => 
        (term) 
          // If the term exists make a request
          ? this.apiService.search({ limit: this.searchResultsLimit, term: term })
          // Otherwise, stop all processing
          : EMPTY
      )
    )
);
mtpultz
  • 17,267
  • 22
  • 122
  • 201
0

Note to self... if you're going crazy thinking that null is still passing by the filter, then make sure it's not actually a string like 'null'.

Adam Cox
  • 3,341
  • 1
  • 36
  • 46