Search code examples
angularrxjsangular-routerasync-pipe

How to show loading template using OnPush change detection?


So I'm listening to a route param:

searchResults$: Observable<string[]>;

constructor(
  private route: ActivatedRoute,
  private svc: SearchService) { 
  this.searchResults$ = this.route.paramMap.pipe(
    map((p: ParamMap) => p.get('searchTerm')),
    switchMap((searchTerm: string) => this.svc.getSearchResults(searchTerm))
  );
}

...and I'm unwrapping it with async pipe...

<p>Search Results</p>

<ul *ngIf="searchResults$ | async as results; else loading">  
    <li *ngFor="let r of results">
        {{ r }}
    </li>
</ul>

<ng-template #loading>Loading!!11!</ng-template>

I'm using ChangeDetectionStrategy.OnPush, and would like to continue to do so

This works terrific whenever the component is first initialized.

However, if I use the angular router to supply a different route param (while the component is still loaded):

this.router.navigate(['search/${searchTerm}`])

then the searchResult$ observable is never undefined and else loading never gets called. A new list merely replaces the old one without indicating to the user that loading is occurring.

Is there an OnPush friendly way to get the loading template to show when the route paramMap observable supplies a new value?

I've tried going back to Default change detection and throwing a bunch of tap(() => this.isloading = true;) into my observable composition- but I don't want to do that, I'd like to see if I can do this while using OnPush change detection.

StackBlitz Here


Solution

  • You can simply have your switchMapped observable startWith undefined:

      this.searchResults$ = this.route.paramMap.pipe(
        map(p => p.get('searchTerm')),
        switchMap(searchTerm => this.svc.getSearchResults(searchTerm).pipe(
          startWith(undefined)
        ))
      );