I am wondering why my loading spinner is not firing, and I think it may be due to my confusion in regard to my LoadingService at the app level, and my LoadingService as a provider at the page level.
APP LEVEL
app.component.html
<app-header></app-header>
<messages></messages>
<loading></loading>
<router-outlet></router-outlet>
app.module.ts
@NgModule({
declarations: [
...
LoadingComponent,
...
]
app.component.ts
providers: [LoadingService]
LOADING COMPONENT AND SERVICE
loading.component.html
<div class="spinner-container" *ngIf="loadingService.loading$ | async">
<mat-spinner></mat-spinner>
</div>
loading.service.ts
import { Injectable } from "@angular/core";
import { BehaviorSubject, Observable, of } from "rxjs";
@Injectable()
export class LoadingService {
private loadingSubject = new BehaviorSubject<boolean>(false);
loading$: Observable<boolean> = this.loadingSubject.asObservable();
loadingOn() {
this.loadingSubject.next(true);
}
loadingOff() {
this.loadingSubject.next(false);
}
}
PAGE THAT USES THE LOADER
page.component.ts
@Component({
...
providers: [LoadingService]
})
constructor(private loadingService: LoadingService) { }
ngOnInit() {
...
this.reloadParameters();
...
}
reloadParameters() {
this.loadingService.loadingOn();
this.parameters$ = this.adminService.getParameters().pipe(
catchError(err => {
const message = "Could Not Load Parameters";
this.messagesService.showErrors(message);
console.log(message, err);
return throwError(err);
}),
finalize(() => this.loadingService.loadingOff())
);
}
A console.log tells me it hits the loadingOn() method and the loading$ observable is true. But no spinner, even when I comment out the api call which should just trigger the spinner indefinitely. But instead, it doesn't trigger the spinner at all.
Based on how you have your components set up, it seems like you should just use providedIn: root
in your service instead of adding LoadingService
to the providers
array of several different components.
From the angular docs:
You should always provide your service in the root injector unless there is a case where you want the service to be available only if the consumer imports a particular @NgModule.
@Injectable({
providedIn: 'root'
})
export class LoadingService {
...
}
There is an even more interesting answer as to why you are seeing this issue:
I am wondering why my loading spinner is not firing, and I think it may be due to my confusion in regard to my LoadingService at the app level, and my LoadingService as a provider at the page level.
A console.log tells me it hits the loadingOn() method and the loading$ observable is true. But no spinner, even when I comment out the api call which should just trigger the spinner indefinitely. But instead, it doesn't trigger the spinner at all.
This issue is caused by the way you are providing the service in several different components.
From the docs:
When you register a provider at the component level, you get a new instance of the service with each new instance of that component.
In other words, the instance of LoadingService that PageComponent is using is not the same instance that LoadComponent is using. If you want a global shared instance of the service (which I think is the most common use case for Angular Services), providedIn: root
in the service itself (see short answer) is all you'll need to do.
DI is one of the more complicated topics to learn about regarding Angular. There is official documentation covering it, but I would highly recommend this video explanation / playlist on YouTube.