Reputation: 3498
I'm trying to implement a global loading indicator that can be reused in the entire application. I have an injectable service that has the show and hide functions:
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable()
export class SpinnerOverlayService {
private loaderSubject = new Subject<any>();
public loaderState = this.loaderSubject.asObservable();
constructor() { }
/**
* Show the spinner
*/
show(): void {
this.loaderSubject.next(<any>{ show: true });
}
/**
* Hide the spinner
*/
hide(): void {
this.loaderSubject.next(<any>{ show: false });
}
}
And this is the code of the spinner overlay component. I'll exclude details about the HTML and CSS implementation as they're not important here.
import { Component, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { SpinnerOverlayService } from '../spinner-overlay.service';
@Component({
selector: 'spinner-overlay',
templateUrl: './spinner-overlay.component.html',
styleUrls: ['./spinner-overlay.component.scss']
})
export class SpinnerOverlayComponent implements OnInit {
show = false;
private _subscription: Subscription;
constructor(private spinnerOverlayService: SpinnerOverlayService) { }
ngOnInit(): void {
this._subscription = this.spinnerOverlayService.loaderState.subscribe((state) => {
console.log("Subscription triggered.");
this.show = state.show;
});
}
ngOnDestroy(): void {
this._subscription.unsubscribe();
}
}
The problem: In the code of the overlay component I'm subscribing to the observable loaderState of the service. However when I call the show() function which triggers the next() of the observable, the subscription callback is not triggered.
This is how I call the show() function in the app.component.ts:
ngOnInit() {
this.spinnerOverlayService.show();
}
What could I be missing? Seems really strange that the callback is not triggered.
Here is an example in Stackblitz: https://stackblitz.com/edit/angular-7-registration-login-example-2qus3f?file=app%2Fspinner-overlay%2Fspinner-overlay.component.ts
Upvotes: 4
Views: 4212
Reputation: 11982
In addition to the above answer you can have a state in your spinnerOverlayService service to check the show hide and also have a subject to subscribe if new value is ready:
public state = { show: false };
constructor() { }
/**
* Show the spinner
*/
show():void {
this.state = { show: true };
this.loaderSubject.next(<any>{ show: true })
}
/**
* Hide the spinner
*/
hide():void {
this.state = { show: false };
this.loaderSubject.next(<any>{ show: false })
}
and in your ngOnInit:
ngOnInit(): void {
if(this.spinnerOverlayService.state.show){
console.log('Subscription triggeredd.');
};
this._subscription = this.spinnerOverlayService.loaderState.subscribe((state) => {
console.log("Subscription triggered.");
this.show = state.show;
});
}
OR you can use:
private loaderSubject = new ReplaySubject(1); // to cache last value
demo.
Upvotes: 2
Reputation: 8859
The problem is you call this.spinnerOverlayService.show();
before spinner-overlay
is initialized. Subject
s do not hold previous emitted value, so late subscribers won't get any value unless there is a new value.
One thing you can do is to change Subject
to BehaviorSubject
which emits the last value to new subscribers.
Or, you can call this.spinnerOverlayService.show();
within ngAfterViewInit
.
This way, you'll know spinner-overlay
will get initialized and subscribe to spinnerOverlayService.loaderState
ngAfterViewInit() {
this.spinnerOverlayService.show();
}
Upvotes: 4