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.
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
}
]