Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2 loader on each http request

What i am trying to do is:
I want to use the spinner whenever a http request accurs. In other words i want user to see a loading screen whenever a http request happens in my app.component.
My spinner.component and spinner-service files are same with the answer in this question.
And my app.component's component is

@Component({
    selector: 'todoApi',
    template: `
        <div class="foo">
            <spinner-component></spinner-component>
            <h1>Internship Project</h1>
            <a [routerLink]="['Dashboard']">Dashboard</a>
            <a [routerLink]="['Tasks']">List</a>
            <router-outlet></router-outlet>
        <div>
    `,
    directives: [ROUTER_DIRECTIVES,SpinnerComponent],
    providers: [
        ROUTER_PROVIDERS,
    ]
})

@RouteConfig([
    {
        path: '/dashboard',
        name: 'Dashboard',
        component: DashboardComponent,
        useAsDefault: true
    },{
        path: '/tasks',
        name: 'Tasks',
        component: TaskComponent
    },{
        path: '/detail/:id',
        name: 'TaskDetail',
        component: TaskDetailComponent
    },
])

To conclue , whenever a http request occurs in one of these routes, i want to show the spinner to user. I know this has been a bad question , but i am new to angular 2 and i would realy be grateful if anyone could help me with that.
Thanks alot!
Edit!:
Solution with Günther's answer: I wrapped my http and spinner-service into a HttpClient component and used it instead of regular http module. Here is my HttpClient component:

import { Injectable } from '@angular/core';
import { Http, Headers } from '@angular/http';
import { SpinnerService} from './spinner-service';

@Injectable()
export class HttpClient {
  constructor(
      private http: Http,
      public spinner: SpinnerService
    ){

  }

  createAuthorizationHeader(headers:Headers) {
    headers.append('Authorization', 'Basic ' + btoa('username:password')); 
  }

  get(url) {
    this.spinner.start();
    let headers = new Headers();
    this.createAuthorizationHeader(headers);
    return this.http.get(url, { headers: headers }).do(data=> {this.spinner.stop()});
  }

  post(url, data) {
    this.spinner.start();
    let headers = new Headers();
    this.createAuthorizationHeader(headers);
    return this.http.post(url, data, { headers: headers }).do(data=> {this.spinner.stop()});
  }
}
like image 355
ozata Avatar asked Jul 01 '16 11:07

ozata


3 Answers

Use a service that is shared between Http (there are answers already how about wrapping Http in your own class) and the <spinner-component>. See also https://angular.io/docs/ts/latest/cookbook/component-communication.html

In the shared service maintain a counter of started (increase) and completed/failed HTTP requests and notify the <spinner-component> every time when the counter changes from 0 to >0 or from >0 to 0 to enable or disable itself.

like image 153
Günter Zöchbauer Avatar answered Oct 29 '22 09:10

Günter Zöchbauer


Thanks for your answer Günter Zöchbauer's, An example which I built based on my needs. I did not use an HTTP wrapper which would be easier to use, however, this example works with multiple services calls based on your counter suggestion. Hope it helps someone :)

  1. Create the Loader service.

    import { Injectable } from '@angular/core';
    import { BehaviorSubject } from 'rxjs/BehaviorSubject';
    
    @Injectable()
    
    export class LoaderService {
        public loaderCounter: BehaviorSubject<number> = new BehaviorSubject<number>(0);
        displayLoader(value: boolean) {
          let counter = value ? this.loaderCounter.value + 1 : this.loaderCounter.value - 1;
          this.loaderCounter.next(counter);
        }
    }
    
  2. Include the service within the providers of your maain module file (Ex: AppModule)

  3. In your main component file (Ex: AppComponent), subscribe to the changes and reflect to the loader (in my case it's a seperate component).

    //Imports
    import { Subscription } from 'rxjs/Subscription';
    import { LoaderService } from './core/loader.service';
    ..
    @Component({
      selector: 'my-app',
      template: `
        <div class="container-fluid content">
          <router-outlet></router-outlet>
        </div>
        <spinner [visible]="displayLoader"></spinner>
      `
    })
    
    export class AppComponent implements OnInit, OnDestroy {
        displayLoader: boolean;
        loaderSubscription: Subscription;
        constructor(private loaderService: LoaderService) {}
    
        ngOnInit() {
            this.loaderSubscription = this.loaderService.loaderCounter.subscribe((counter: number) => {
                this.displayLoader = counter != 0;
            });
        }
    
        ngOnDestroy() {
            this.loaderSubscription.unsubscribe();
        }
    }
    
  4. Using the loader service:

     import { LoaderService } from './core/loader.service';
        ..
        export class SampleComponent implements OnInit {
            constructor(private _service: SomeService, private loader: LoaderService) { }
    
        ngOnInit() {
            this.loader.displayLoader(true);
            this._service.getBalance().subscribe(
                response => ..do something..,
                () => .. error..,
                () => this.loader.displayLoader(false)
            );
        }
    }
    
like image 29
Mark Cutajar Avatar answered Oct 29 '22 08:10

Mark Cutajar


Just for the people who gets here from now on...

With this solution the spinner will not stop in case of error with the http request. Make sure you do the following:

...
return this.http.post(url, data, { headers: headers })
  .do(data=> {this.spinner.stop()},
  err=> {this.spinner.stop());
...
like image 1
Gabriel Avatar answered Oct 29 '22 10:10

Gabriel