How to Show spinner for every HTTP requests in angular 5?
Asked Answered
A

8

10

i am new to angular 5 . How to code a common function to show spinner for every HTTP request in angular 5.Please help me to implement this.

Ante answered 30/4, 2018 at 12:17 Comment(3)
You could use PaceJs, it's not angular specific,but it does this out of the boxOrvil
I think it is not good idea to show single spinner for ANY xhr request. You will probably need more details in the futurePigling
#48386127Tropology
L
17

You can use Angular HttpInterceptor to show a spinner for all your requests, Here's a good medium article on how to implement an http interceptor

Also, you will have to create a spinner service/module and inject it in your http interceptor. Finally, in your intercept method you can use the finally rxJs method to stop your spinner. Here's a simple implementation:

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    this.spinnerService.start();
    return next.handle(req).finally(res => this.spinnerService.stop() );
}

Enjoy!

Bonus: Here's a spinner service implementation example

Laureen answered 30/4, 2018 at 12:35 Comment(1)
Probably dup of: #49385869Laureen
R
4

Source Link

Create a service

//loader.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class LoaderService {

  public isLoading = new BehaviorSubject(false);
  constructor() { }
}

Create Loader Interceptor

    // loader.interceptors.ts
    import { Injectable } from '@angular/core';
    import {
        HttpErrorResponse,
        HttpResponse,
        HttpRequest,
        HttpHandler,
        HttpEvent,
        HttpInterceptor
    } from '@angular/common/http';
    import { Observable } from 'rxjs';
    import { LoaderService } from './loader.service';

    @Injectable()
    export class LoaderInterceptor implements HttpInterceptor {
        private requests: HttpRequest<any>[] = [];

        constructor(private loaderService: LoaderService) { }

        removeRequest(req: HttpRequest<any>) {
            const i = this.requests.indexOf(req);
            if (i >= 0) {
                this.requests.splice(i, 1);
            }
            this.loaderService.isLoading.next(this.requests.length > 0);
        }

        intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

            this.requests.push(req);
            console.log("No of requests--->" + this.requests.length);
            this.loaderService.isLoading.next(true);
            return Observable.create(observer => {
                const subscription = next.handle(req)
                    .subscribe(
                        event => {
                            if (event instanceof HttpResponse) {
                                this.removeRequest(req);
                                observer.next(event);
                            }
                        },
                        err => {
                            alert('error returned');
                            this.removeRequest(req);
                            observer.error(err);
                        },
                        () => {
                            this.removeRequest(req);
                            observer.complete();
                        });
                // remove request from queue when cancelled
                return () => {
                    this.removeRequest(req);
                    subscription.unsubscribe();
                };
            });
        }
    }

Now create a loader component then add in the app component

  //loader.interceptor.ts
  import { Component, OnInit } from '@angular/core';
  import { LoaderService } from '../loader.service';

  @Component({
    selector: 'app-loading',
    templateUrl: './loading.component.html',
    styleUrls: ['./loading.component.css']
  })
  export class LoaderComponent implements OnInit {

    loading: boolean;
    constructor(private loaderService: LoaderService) {
      this.loaderService.isLoading.subscribe((v) => {
        console.log(v);
        this.loading = v;
      });
    }
    ngOnInit() {
    }

  }
Reign answered 28/4, 2019 at 8:20 Comment(0)
C
3

The complete guide is here with a material mat-progress-spinner usage. Very cool!

Conjugal answered 12/7, 2019 at 16:29 Comment(0)
C
1

This has nothing to do with HttpClient or HTTP Requests. It is a question of how to handle asynchronous calls in general (HTTP or not).

You should have

<div class="spinner" *ngIf="loading"; else showWhenLoaded"><div>
<ng-template #showWhenLoaded>
    <div>Your Content</div>
</ng-template>

and in the ts-file:

loading: boolean = true;

methodToTriggerRequest() {
    this.loading = true;
    this.http.get(...).subscribe(response => {
        if (resposnseNotAnError(response)) {this.loading = false}
    })
}
Changchangaris answered 30/4, 2018 at 12:27 Comment(2)
In this way how many loaders are you going to introduce to the application?Senate
one for each item that needs to be fetched individually. You don't need to block the entire app with a loader if you fetch dynamic data that is used only in one part of the screenChangchangaris
S
0

You can create a service and then publish events to it while in the root level of the application subscribed to it. Let me explain.

broadcast.service.ts (this is your service)

import { Injectable } from '@angular/core';

import { Subject } from 'rxjs/Subject';
import { Subscription } from 'rxjs/Subscription';

import 'rxjs/add/operator/filter'
import 'rxjs/add/operator/map'

/**
 * This class acting as event service bus for the entire app.
 * No Need to register the class as this registered in the root level.
 * Can just inject to componets.
 */
@Injectable()
export class BroadcastService {

    private _handler: Subject<Message> = new Subject<Message>();

    broadcast(type: string, payload: any = null) {
        this._handler.next({ type, payload });
    }

    subscribe(type: string, callback: (payload: any) => void): Subscription {
        return this._handler
            .filter(message => message.type === type)
            .map(message => message.payload)
            .subscribe(callback);
    }
}

interface Message {
    type: string;
    payload: any;
}

Then in you can publish and subscribe to events like this:

your service level:

this.broadcastService.broadcast('PROGRESS_START');

In your app component level:

this.broadcastService.subscribe('PROGRESS_START', ()=>{
  //hit when you start http call
  this.myLoader = true;
});

finally in app.component.html:

<div *ngIf="myLoader">
 <!--YOUR LOADER SHOULD GO HERE-->
</div>
<router-outlet></router-outlet>

This is very generic loosely coupled way to implement any loader you want.

Senate answered 30/4, 2018 at 12:38 Comment(2)
He could also implement NGRX, but this is not what he askedChangchangaris
He asked about "common function to show spinner for every HTTP request". So in this way he can just publish progress start event from anywhere in the application.Senate
F
0

Here I have written some steps to create your own custom progressbar.

  1. Create a component

    import { Component, Input } from '@angular/core'; @Component({ selector: 'app-sw-progressbar', template: <div *ngIf="loading"> <mat-progress-bar style="position:absolute; left:15px; width: calc(100% - 30px); height:5px;" color="primary" mode="indeterminate"> </mat-progress-bar> </div>, }) export class SWProgresbarComponent { @Input() loading = false; constructor() { } }

2 Import component in module:-

import { SWProgresbarComponent } from '../sw-progressbar.component';
@NgModule({
declarations: [SWProgresbarComponent],
imports: [
CommonModule,
MaterialModule,
RouterModule
],
exports: [SWProgresbarComponent,
RouterModule
]
})
export class SharedModule { }
  1. How to use it?

    Here loading would be true in the component where you want to show. Add this below code in the component where you want to show the loader.

    <app-sw-progressbar [loading]="loading"></app-sw-progressbar>

    For spinner replace mat-progress-bar by mat-progress-spinner.

Febricity answered 7/5, 2020 at 8:18 Comment(0)
S
0

Create interceptor service

import { Injectable } from '@angular/core';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';
import { finalize } from 'rxjs/operators';
import { NgxSpinnerService } from 'ngx-spinner';

@Injectable()
export class SpinnerInterceptor implements HttpInterceptor {

    constructor(private spinner: NgxSpinnerService) { }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        this.spinner.show();
        return next.handle(req).pipe(
            finalize(() => this.spinner.hide())
        );
    }
}

Provide this service in app module

import { HTTP_INTERCEPTORS } from '@angular/common/http';
                 
        ....
             
@NgModule({       
    providers: [
        { provide: HTTP_INTERCEPTORS, useClass: SpinnerInterceptor, multi: true }               
    ]
})

Last in app.component inside the router-outlet add your spinner tags

 <router-outlet>
    <ngx-spinner bdColor="rgba(0,0,0,0.8)"
                 size="medium"
                 color="#fff"
                 type="timer">
        <p style="color: #fff"> Loading... </p>
    </ngx-spinner>
</router-outlet>

As you can see I am using NgxSpinner but this should make no difference if you're using custom spinner, you just need to create service to show and hide the spinner and inject this service in the spinner interceptor.

Spotted answered 9/6, 2021 at 9:34 Comment(0)
A
-1

In Angular's interceptor I have used "do" operator.

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    // handler for both success and fail response
    const onReqFinish = (event: HttpEvent<any>) => {
      if (event.type === 4) {
        this.onXhrFinish();
      }
    };

    this.onXhrStart();

    return next.handle(req)
     .do(
       onReqFinish,
       onReqFinish
     );
}

onXhrStart function shows a loader and onXhrFinish hides it.

Entire, working source code and demo are here

Acetify answered 30/7, 2018 at 9:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.