1

struggling with angular and observables.

I have a observable like this:

 private searchResults: Observable<any[]>;

and I fill this with calling it like:

this.searchResults = this.searchTerms.pipe(
        map((event: any) => event.target.value),
        debounceTime(300),
        distinctUntilChanged(),
        filter((term: string) => term && term.length >= 3),
        flatMap((term: string) => this.http.get<any[]>(`https://my-cool-rest.api/${term}`))
    )

(note: searchTerms is Subject<string>() and is implemented on html like: <input type="text" (keyup)="searchTerms.next($event)"/>)

everything so far is ok, all is working as expected.

When I receive the results, I show them in a simple UL

<ul>
     <li *ngFor="let result of searchResults | async">
          <a (click)="selectItem(result.id)>{{ result.name }}</a>
     </li>
</ul>

Clicking on a item will call the selectItem function that write the results on console and clear the searchResults:

selectItem(result: string) {
    if (result) {
        console.log(result);
        this.searchResults = Observable.of([]);
    }
}

Again, everything is ok.

But if I repeat the process to search for other keywords, *ngFor does not show result anymore.

To be completely secure, I subscribed to searchResults (e.g.: in constructor) to log it

constructor() {
     this.searchResults.subscribe(data => console.log(data));
}

and guess what? Works like a charm

I suppose this is an angular (5+) behavior I don't understand.

Thanks in advance!

Valerio
  • 3,297
  • 3
  • 27
  • 44

1 Answers1

1

As undefined user pointed out, you're overriding the stream with this line:

this.searchResults = Observable.of([]);

So if you want to reset the array, you have two options:
- set the "search" input to an empty string, but I'd trigger another request for nothing
- take advantage of observables and stream combinations

I think the second one is a better idea and here's how to implement it:

import { merge } from 'rxjs/observable/merge';
import { mapTo } from 'rxjs/operators';

private searchResults: Observable<any[]>;
private resetResults$ = new Subject();

this.searchResults = merge(
  // every time the reset stream receive a new value,
  // merge it into the searchResults and no matter what 
  // the value is merge an empty array
  resetResults$.pipe(mapTo([])),

  this.searchTerms.pipe(
    map((event: any) => event.target.value),
    debounceTime(300),
    distinctUntilChanged(),
    filter((term: string) => term && term.length >= 3),
    flatMap((term: string) => this.http.get<any[]>(`https://my-cool-rest.api/${term}`))
  )
)

selectItem(result: string) {
  if (result) {
    // send a new value to the reset stream
    this.resetResults$.next();
  }
}
maxime1992
  • 22,502
  • 10
  • 80
  • 121