loading spinner using rxjs BehaviorSubject

admin

Administrator
Staff member
I created a loading spinner component for my <strong>angular 4</strong> app that should show during AJAX calls but I have trouble displaying it with a subscription to a <strong>BehaviorSubject</strong>.

This question is related to <a href="https://stackoverflow.com/questions...ime-data-recieved-from-the-server-in-angular2">how to show a spinner till the time data recieved from the server in angular2</a> but not a duplicate (as I want to do it with a reusable component) and <a href="https://stackoverflow.com/questions...js-behaviorsubject-subscribe-call-not-working">Angular 2 + RxJS BehaviorSubject subscribe call not working</a> (Though I don't see any difference between my code and the accepted answer. Did I miss something?)

I basically followed this tutorial <a href="https://hassantariqblog.wordpress.c...-spinner-as-service-in-angular-2-application/" rel="nofollow noreferrer">https://hassantariqblog.wordpress.c...-spinner-as-service-in-angular-2-application/</a>

Here's some of my code:

<strong>app.modules.ts</strong>

Code:
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 {
}

<strong>app.component.html</strong>

Code:
&lt;router-outlet&gt;&lt;/router-outlet&gt;
&lt;app-loader&gt;&lt;/app-loader&gt;

<strong>loader.component.html</strong>

Code:
&lt;div class="loader" *ngIf="isLoading"&gt;&lt;/div&gt;

<strong>loader.component.ts</strong>

Code:
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) =&gt; {
            this.isLoading = val;
        });
    }
}

<strong>loader.service.ts</strong>

Code:
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';

@Injectable()
export class LoaderService {
    public status: BehaviorSubject&lt;boolean&gt; = new BehaviorSubject&lt;boolean&gt;(false);

    display(value: boolean) {
        console.log('LoaderService.display ' + value);
        this.status.next(value);
    }
}

<strong>A method in some service that does an AJAX call</strong>

Code:
constructor(private http: Http, private loaderService: LoaderService) {
}

getSomeStuff(): Observable&lt;SomeItem[]&gt; {
    // show the loading spinner
    this.loaderService.display(true);

    const content = this.http.get(this.apiUrl)
        .map(this.extractData);

    content.subscribe(
        () =&gt; {
            // hide the loading spinner
            this.loaderService.display(false);
        }
    );

    return content;
}

The problem is that the loader never gets shown, because
Code:
isLoading
is never set to
Code:
true
. The console output:

Code:
LoaderComponent subscription false
LoaderService.display true
LoaderService.display false

So the subscription to the loaderService, that changes the value of
Code:
isLoading
in loader.component.ts only gets called once, when initializing the component. But as far as I understand how <strong>BehaviorSubject</strong> should work, it should be called, whenever
Code:
LoaderService.status
changes. Why not? What am I doing wrong?