Reputation: 3038
I created a loading spinner component for my angular 4 app that should show during AJAX calls but I have trouble displaying it with a subscription to a BehaviorSubject.
This question is related to how to show a spinner till the time data recieved from the server in angular2 but not a duplicate (as I want to do it with a reusable component) and Angular 2 + RxJS BehaviorSubject subscribe call not working (Though I don't see any difference between my code and the accepted answer. Did I miss something?)
I basically followed this tutorial https://hassantariqblog.wordpress.com/2017/03/22/angular2-using-custom-loader-spinner-as-service-in-angular-2-application/
Here's some of my code:
app.modules.ts
import {AppComponent} from './app.component';
import {AppRoutingModule} from './app-routing.module';
import {SomeComponent} from './some.component';
import {LoaderComponent} from './loader/loader.component';
import {LoaderService} from './loader/loader.service';
@NgModule({
declarations: [
AppComponent,
LoaderComponent,
SomeComponent
],
imports: [
BrowserModule,
NoopAnimationsModule,
AppRoutingModule
],
providers: [
LoaderService
],
bootstrap: [AppComponent]
})
export class AppModule {
}
app.component.html
<router-outlet></router-outlet>
<app-loader></app-loader>
loader.component.html
<div class="loader" *ngIf="isLoading"></div>
loader.component.ts
import {Component, OnInit} from '@angular/core';
import {LoaderService} from './loader.service';
@Component({
selector: 'app-loader',
templateUrl: 'loader.component.html',
providers: [LoaderService]
})
export class LoaderComponent implements OnInit {
isLoading: boolean;
constructor(private loaderService: LoaderService) {
}
ngOnInit() {
this.loaderService.status.subscribe((val: boolean) => {
this.isLoading = val;
});
}
}
loader.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
@Injectable()
export class LoaderService {
public status: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
display(value: boolean) {
console.log('LoaderService.display ' + value);
this.status.next(value);
}
}
A method in some service that does an AJAX call
constructor(private http: Http, private loaderService: LoaderService) {
}
getSomeStuff(): Observable<SomeItem[]> {
// show the loading spinner
this.loaderService.display(true);
const content = this.http.get(this.apiUrl)
.map(this.extractData);
content.subscribe(
() => {
// hide the loading spinner
this.loaderService.display(false);
}
);
return content;
}
The problem is that the loader never gets shown, because isLoading
is never set to true
. The console output:
LoaderComponent subscription false
LoaderService.display true
LoaderService.display false
So the subscription to the loaderService, that changes the value of isLoading
in loader.component.ts only gets called once, when initializing the component. But as far as I understand how BehaviorSubject should work, it should be called, whenever LoaderService.status
changes. Why not? What am I doing wrong?
Upvotes: 4
Views: 6952
Reputation: 58400
The problem is due to this line in LoaderComponent
:
providers: [LoaderService]
By specifying the service using a component-level provider, you are informing Angular that the component is to receive its own instance of the service. That's not what you want; you want to share the module-level instance.
Just remove that line from LoaderComponent
so that a single instance of the service is used.
Upvotes: 3