Dev Choudhary
Dev Choudhary

Reputation: 105

Angular: Can't use async pipe

I am trying to show a spinner using a observable which resolves to a boolean value using a async pipe in a template. The value of the observable is set in the service. I am not able to figure out whats going wrong. The observable value seems to resolve to undefined. Why this could be not working?

Here is the stackblitz created for the example.

Upvotes: 0

Views: 795

Answers (2)

deerawan
deerawan

Reputation: 8443

It is undefined because we run setLoader inside ngOnInit where view is not ready. At that time, loading$ in service class is still undefined. When template renders it, definitely it is undefined.

I'd move the setLoader to ngAfterViewInit

import { Component, VERSION, AfterViewInit } from '@angular/core'; // import AfterViewInit
import { MyService } from './my.service';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements AfterViewInit { // add AfterViewInit
 constructor(public myservice: MyService) {
 }

 ngOnInit() {

 }

 ngAfterViewInit() {
   this.myservice.setLoader(true); // move it here
 }
}

Also I checked that there's a typo in the template, the original code uses myservice.loader$ but it is supposed to be myservice.loading$.

<div *ngIf="myservice.loading$ | async">
  Loading
</div>

Hope it helps

Upvotes: 1

Poul Kruijt
Poul Kruijt

Reputation: 71961

You should not use a Subject(), or do the setLoader in ngAfterViewInit, at the moment you do .next(true) the template is not subscribed to it yet:

Solution 1:

export class AppComponent {
   loading$: Observable<boolean>;

   constructor(private myservice: MyService) {
     this.loading$ = myservice.loading$;
   }

   ngAfterViewInit() {
     this.myservice.setLoader(true);
   }
}

Solution 2:

export class AppComponent {
   loading$: Observable<boolean>;

   constructor(private myservice: MyService) {
     this.loading$ = myservice.loading$.pipe(
       shareReplay(1)
     );
   }

   ngOnInit() {
     this.myservice.setLoader(true);
   }
}

Solution 3:

private loadingSource = new ReplaySubject(1);

loading$ = this.loadingSource.asObservable();

constructor() { }

setLoader(isLoading: boolean) {
  this.loadingSource.next(isLoading);
}

Upvotes: 1

Related Questions