Angular 8 Async Pipe subscription issue

811 Views Asked by At

Full StackBlitz example: https://stackblitz.com/edit/angular8-async-pipe

There are three identical components in app component template:

<app-loader></app-loader>
<app-progress></app-progress>
<app-spinner></app-spinner>

Every component dispatches it's own NgRx action, which fires an Http request using NgRx effects and waits for its completion:

import { Component, OnInit } from '@angular/core';

import { Store } from '@ngrx/store';

import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';

import { ActionsService } from '../../../services/actions.service';

import * as MockyActions from '../../actions/mocky.actions';

@Component({
  selector: 'app-spinner',
  templateUrl: './spinner.component.html'
})
export class SpinnerComponent implements OnInit {
  progress$: Observable<boolean>;

  constructor (
    private store: Store<any>,
    private actionsService: ActionsService) {}

  ngOnInit() {
    this.progress$ = this.actionsService
      .isInProgress(MockyActions.GetMockySpinner)
      .pipe(
        map(({status}) => status),
        tap((status) => console.log('GetMockySpinner: ', status))
      );

    this.store.dispatch(MockyActions.GetMockySpinner());
  }
}

In every component using Async Pipe I want to show spinner while Http request is in progress:

<div class="text-center" *ngIf="!(progress$ | async); else progress">
  Spinner
</div>

<ng-template #progress>
  <div class="text-center">
    <span class="spinner spinner-sm"></span>
  </div>
</ng-template>

But only spinner in <app-spinner></app-spinner> component is displaying during Http request progress in this case. And if the sequence of components in app component template changed:

<app-loader></app-loader>
<app-progress></app-progress>
<app-spinner></app-spinner>
<app-loader></app-loader>

Spinner in <app-loader></app-loader> component is displaying during Http request progress in this case. So only last component in template works as expected.

So what's changed with Async Pipe in Angular 8 or what I'm doing wrong?

Full StackBlitz example: https://stackblitz.com/edit/angular8-async-pipe

2

There are 2 best solutions below

5
On BEST ANSWER

You aren't using an actual state and reducer for your app. NgRX requires that you use both actions and reducers. The problem you are having is that by the time the first two components have subscribed to the actions subject, it is the third action that is there. You don't listen for actions to occur in NgRX. What you do instead is watch for your state to be modified by the action.

Here is a working stackblitz that fixes your issue.

https://stackblitz.com/edit/angular8-async-pipe-xexehj?file=src%2Fapp%2Fapp.module.ts

Below is some helpful documentation. https://ngrx.io/guide/store/reducers https://ngrx.io/guide/store

1
On

Something is wrong with your MockyActions.

Change

this.progress$ = this.actionsService.isInProgress(MockyActions.GetMockyProgress)
...

and

this.progress$ = this.actionsService.isInProgress(MockyActions.GetMockyLoader)
...

To

this.progress$ = this.actionsService.isInProgress(MockyActions.GetMockySpinner)

and you example works fine.