Overlay with spinner

前端 未结 6 1203
忘了有多久
忘了有多久 2021-01-30 16:15

I\'m trying to create an overlay that overlays a page with a spinner in the middle. What\'s the simplest way to accomplish this? I only need to worry about IE 8 and above.

6条回答
  •  一向
    一向 (楼主)
    2021-01-30 17:00

    As an update, for Angular 7, a very good example, loading plus http interceptor, here: https://nezhar.com/blog/create-a-loading-screen-for-angular-apps/.

    For version 6, you need a small adjustment when you use Subject. You need to add the generic type.

    loadingStatus: Subject = new Subject();
    

    I'm using angular material, so instead of a loading text, you can use mat-spinner.

    
    

    Update: the code from the previous page will not complete work (regarding the interceptor part), but here you have the complete solution: https://github.com/nezhar/snypy-frontend

    And as Miranda recommended into comments, here is also the solution:

    The loading screen component:

    loading-screen.component.ts

        import { Component, ElementRef, ChangeDetectorRef, OnDestroy, AfterViewInit } from '@angular/core';
    import { Subscription } from 'rxjs';
    import { LoadingScreenService } from '../services/loading-screen.service';
    
    @Component({
      selector: 'app-loading-screen',
      templateUrl: './loading-screen.component.html',
      styleUrls: ['./loading-screen.component.css']
    })
    export class LoadingScreenComponent implements AfterViewInit, OnDestroy {
    
      loading: boolean = false;
      loadingSubscription: Subscription;
    
      constructor(
        private loadingScreenService: LoadingScreenService,
        private _elmRef: ElementRef,
        private _changeDetectorRef: ChangeDetectorRef
      ) { }
    
      ngAfterViewInit(): void {
        this._elmRef.nativeElement.style.display = 'none';
        this.loadingSubscription = this.loadingScreenService.loadingStatus.pipe().subscribe(
          (status: boolean) => {
            this._elmRef.nativeElement.style.display = status ? 'block' : 'none';
            this._changeDetectorRef.detectChanges();
          }
        );
      }
    
      ngOnDestroy() {
        console.log("inside destroy loading component");
        this.loadingSubscription.unsubscribe();
      }
    
    }
    

    loading-screen.component.html

    loading-screen.component.css

      #overlay {
          position: fixed; /* Sit on top of the page content */
          display: block; /* Hidden by default */
          width: 100%; /* Full width (cover the whole page) */
          height: 100%; /* Full height (cover the whole page) */
          top: 0;
          left: 0;
          right: 0;
          bottom: 0;
          background-color: rgba(60, 138, 255, 0.1); /* Black background with opacity */
          opacity: 0.5;
          z-index: 2; /* Specify a stack order in case you're using a different order for other elements */
          cursor: progress; /* Add a pointer on hover */
      }
    
      .content {
          position: absolute;
          top: 50%;
          left: 50%;
          font-size: 50px;
          color: white;
          transform: translate(-50%, -50%);
          -ms-transform: translate(-50%, -50%);
      }
    

    Don't forget to add the component to your root component. In my case, AppComponent

    app.component.html

    
    

    The service that will manage the component: loading-screen.service.ts

    import { Injectable } from '@angular/core';
    import { Subject } from 'rxjs';
    
    @Injectable({
      providedIn: 'root'
    })
    export class LoadingScreenService {
    
      constructor() { }
    
      private _loading: boolean = false;
      loadingStatus: Subject = new Subject();
    
      get loading(): boolean {
        console.log("get loading: " + this._loading);
        return this._loading;
      }
    
      set loading(value) {
        console.log("get loading: " + value);
        this._loading = value;
        this.loadingStatus.next(value);
      }
    
      startLoading() {
        console.log("startLoading");
        this.loading = true;
      }
    
      stopLoading() {
        console.log("stopLoading");
        this.loading = false;
      }
    }
    

    Here is the http interceptor, which will show/hide the component, using the previous service.

    loading-screen-interceptor.ts

    import { Injectable } from '@angular/core';
    import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
    import { LoadingScreenService } from '../services/loading-screen.service';
    import { Observable } from 'rxjs';
    import { finalize } from 'rxjs/operators';
    
    @Injectable()
    export class LoadingScreenInterceptor implements HttpInterceptor {
    
        activeRequests: number = 0;
    
        constructor(
            private loadingScreenService: LoadingScreenService
        ) { }
    
        intercept(request: HttpRequest, next: HttpHandler): Observable> {
    
            console.log("inside interceptor");
    
            if (this.activeRequests === 0) {
                this.loadingScreenService.startLoading();
            }
    
            this.activeRequests++;
    
            return next.handle(request).pipe(
                finalize(() => {
                    this.activeRequests--;
                    if (this.activeRequests === 0) {
                        this.loadingScreenService.stopLoading();
                    }
                })
            )
        };
    }
    

    And in your app.module.ts, don't forget to config the interceptor

    providers: [
        {
          provide: HTTP_INTERCEPTORS,
          useClass: LoadingScreenInterceptor,
          multi: true
        }
      ]
    

提交回复
热议问题