Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Async pipe subscription not work correctly when nested inside ngIf

I have a quick search box which I would like to have a loading animation. I used ng-template with ngIf to show/hide this animation. And I have some li's nested inside the same div which subscribed to a search result Observable using async pipe to display results. This async pipe works great when there's no *ngIf on the parent div, but seems it is not subscribing anymore when I apply ngIf. Is this an expected behavior? Or am I doing anything wrong?

My markup looks like this.

<input #searchBox type="text" (keyup)="itemLoading=true;searchTerm$.next(searchBox.value)"/>
<div *ngIf="!itemLoading else loading">
<!--Remove ngIf then items will display correctly when search-->
<!-- <div> -->
  <ul>
    <li *ngFor="let item of result$ | async ">{{item}}</li>
  </ul>
</div>
<ng-template #loading>
  <p>LOADING...</p>
</ng-template>

And I am using switchMap to run the search:

private source = of(['apple', 'pear', 'banana']).pipe(delay(500));

  searchTerm$ = new Subject<string>();
  result$: Observable<string[]>;
  itemLoading = false;  

  constructor() {
    this.result$ = this.searchTerm$.pipe(
      tap(term => console.log('search term captured: ' + term)),
      debounceTime(300),
      distinctUntilChanged(),
      switchMap(() => this.source.pipe(
        tap(_ => {
            console.log('source reached');
            this.itemLoading = false;
          })
      ))
    );
  }

When ngIf is present in parent div, the 'source reached' message is never logged in console, as well as the loading template keeps hanging there.

Here is a full working example of what I am talking about: https://stackblitz.com/edit/angular-2ukcqu

like image 235
wctiger Avatar asked Aug 22 '18 20:08

wctiger


People also ask

What is async pipe in angular?

The async pipe allows us to subscribe to an Observable or Promise from the template and returns the value emitted. The async pipes subscribe to the observable when the component loads. It unsubscribes when the component gets destroyed. In this tutorial we will show you how to use async pipe.

How do I use the async pipe with observable?

To use the async pipe, we bind our Observable directly to our component. Unfortunately, by itself, we must use the pipe on every property we want to access. This creates a subscription each time the async pipe is used.

What is the use of async pipe in react?

The async pipe allows us to subscribe to an Observable or Promise from the template and returns the value emitted. The async pipes subscribe to the observable when the component loads. It unsubscribes when the component gets destroyed.

Is the content of ngif still applicable for angular 2 +?

The content is likely still applicable for all Angular 2 + versions. In this article, we are going to cover a new feature introduced in Angular. This feature is a special added syntax to the ngIf statement to make it easier to bind async data to our Angular templates.


1 Answers

Rewriting the *ngIf as hidden should solve the problem. The reason result$ isn't working is because those elements inside the *ngIf won't be added to the dom until itemLoading is false. At that point they'll subscribe to result$, but the event will have already occurred.

Alternatively shareReplay(1) might also do the trick without you having to rewrite anything else, as the reply will run when the Observable is subscribed too.

Solution A

<div [hidden]="itemLoading">
<!-- ... -->
</div>
<div [hidden]="!itemLoading">
  <p>LOADING...</p>
</div>

Solution B

this.result$ = this.searchTerm$.pipe(
  //...
  shareReplay(1)
);
like image 176
Daniel Gimenez Avatar answered Sep 19 '22 00:09

Daniel Gimenez