Angular show spinner for every HTTP request with very less code changes

后端 未结 8 540
Happy的楠姐
Happy的楠姐 2020-12-01 03:08

I am working on an existing Angular application. The version is Angular 4.

The application makes HTTP calls to a REST API from lot of various components.

I w

相关标签:
8条回答
  • 2020-12-01 03:29

    I was searching for something that can be used by every component. I put in a counter, so the spinner stops when every request has finished.

    So this works quite well:

    export class LoadingStatus{
      public counter: number = 0;
      public isLoading = new Subject<boolean>();
    
      public reset(){
        this.counter = 0;
        this.isLoading.next(false);
      }
    }
    
    export function triggerLoading<T>(status: LoadingStatus): (source: Observable<T>) => Observable<T> {
      return (source: Observable<T>): Observable<T> => source.pipe(
        prepare(() => {
          if(status != null){
            status.counter++;
            status.isLoading.next(true)
          }
        }    ),
        finalize(() => {
          if(status != null){
            status.counter--;
            // if there is something like a flikering, maybe use a delay.
            if(status.counter <= 0) {
              status.counter = 0;
              status.isLoading.next(false)
            }
          }
        })
      )
    }
    

    And then call it like:

    public loadingStatus$ = new LoadingStatus();

    public makeRequest(){
       this.myService.load()
        .pipe(triggerLoading(this.loadingStatus$))
        .subscribe(v => {});
    }
    

    HTML:

    <div class="loading-spinner" *ngIf="loadingStatus$?.isLoading | async"></div>
    
    0 讨论(0)
  • 2020-12-01 03:32

    In Angular 5 comes the HttpClient module. You can find more information there.

    With this module, come something called interceptors.

    They allow you to do something for every HTTP request.

    If you migrate from Http to HttpClient (and you should, Http will be deprecated), you can create an interceptor that can handle a variable in a shared service :

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        this.sharedService.loading = true;
        return next
          .handle(req)
          .finally(() => this.sharedService.loading = false);
    }
    

    Now you just have to use this variable into your templates.

    <spinner *ngIf="sharedService.loading"></spinner>
    

    (Be sure to have an injection of your service in the components that display this spinner)

    0 讨论(0)
  • 2020-12-01 03:39

    Depends upon the approach you follow to use REST SERVICES

    My approach is

    • Create a component and place it somewhere in the application level.
    • Create a service which has counter with increment and decrements methods.

    • This service should decide to show the loader(component) or not by following the below steps.

      Increase the counter each for one request from the client.

      Decrease the counter on each response success and failure

    0 讨论(0)
  • 2020-12-01 03:41

    Angular Interceptors can be used in a number of ways as they work pretty well in manipulating and managing HTTP calls to communicate that we make from a client-side web application. We can create an utility for showing Mouse Loader using Interceptor.

    Please go through the below post for the implementation of LoaderInterceptor:-

    Show Loader/Spinner On HTTP Request In Angular using Interceptor

    0 讨论(0)
  • 2020-12-01 03:46

    This is a basic loading dialog that can be toggled with an angular property. Just add *ngIf="loader" to the center-loader and set the property appropriately

    .center-loader {
        font-size: large;
        position:absolute;
        z-index:1000;
        top: 50%;
        left: 50%;
        -ms-transform: translate(-50%, -50%);
        transform: translate(-50%, -50%);
    }
    
    @keyframes blink {50% { color: transparent }}
    .loader__dot { animation: 1s blink infinite; font-size: x-large;}
    .loader__dot:nth-child(2) { animation-delay: 250ms; font-size: x-large;}
    .loader__dot:nth-child(3) { animation-delay: 500ms; font-size: x-large;}
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.3/jquery.min.js"></script>
    <div class="center-loader">
      <strong>Loading
      <span class="loader__dot">.</span>
      <span class="loader__dot">.</span>
      <span class="loader__dot">.</span></strong>
    </div>

    Initialize the loader to true for each page, and then set to false once the service finished:

    Top of component:

    export class MyComponent implements OnInit {
        loader:boolean = true;
    //...
    

    onInit():

     await this.myService
        .yourServiceCall()
        .then(result => {
            this.resultsSet=result);
            this.loader = false;      // <- hide the loader
          }
        .catch(error => console.log(error));
    
    0 讨论(0)
  • 2020-12-01 03:55

    @jornare has a good idea in his solution. He's handling the case for multiple requests. However, the code could be written simpler, without creating new observable and storing requests in memory. Below code also uses RxJS 6 with pipeable operators:

    import { Injectable } from '@angular/core';
    import {
      HttpRequest,
      HttpHandler,
      HttpInterceptor,
      HttpResponse
    } from '@angular/common/http';
    import { finalize } from 'rxjs/operators';
    import { LoadingService } from '@app/services/loading.service';
    import { of } from 'rxjs';
    
    @Injectable()
    export class LoadingInterceptor implements HttpInterceptor {
      private totalRequests = 0;
    
      constructor(private loadingService: LoadingService) { }
    
      intercept(request: HttpRequest<any>, next: HttpHandler) {
        this.totalRequests++;
        this.loadingService.setLoading(true);
    
        return next.handle(request).pipe(
          finalize(res => {
            this.totalRequests--;
            if (this.totalRequests === 0) {
              this.loadingService.setLoading(false);
            }
          })
        );
      }
    }
    

    Add this interceptor service into your module providers:

    @NgModule({
      // ...
      providers: [
        { provide: HTTP_INTERCEPTORS, useClass: LoadingInterceptor, multi: true }
      ]
    })
    export class AppModule { }
    
    0 讨论(0)
提交回复
热议问题