Mon. Aug 2nd, 2021

Just about every project I need to have at least one loading spinner to indicate to the user that data is being loaded and to wait. My recent project I found that to be no exception. I wanted to come up with something that I thought was a little nicer than what I have used in the past.

To give credit where credit is due this was inspired by @divyamember with this stackblitz. Here is my take:

import {Injectable, TemplateRef, ViewContainerRef} from '@angular/core';
import {Overlay, OverlayRef, PositionStrategy} from "@angular/cdk/overlay";
import {TemplatePortal} from "@angular/cdk/portal";

@Injectable({
  providedIn: 'root'
})
export class OverlayService {
  constructor(
      private overlay: Overlay
  ) { }
  createOverlay(config: any): OverlayRef {
    return this.overlay.create(config);
  }
  attachTemplatePortal(overlay: OverlayRef, template: TemplateRef<any>, view: ViewContainerRef) {
    let templatePortal = new TemplatePortal(template, view);
    overlay.attach(templatePortal);
  }

  positionGloballyCenter(): PositionStrategy {
    return this.overlay.position()
        .global()
        .centerHorizontally()
        .centerVertically();
  }
}

My OverlayService, with not real significant changes. Three methods used to setup the overlay.

import {Component, DoCheck, Input, OnInit, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core';
import {OverlayService} from "../../services/overlay.service";
import {OverlayRef} from "@angular/cdk/overlay";

@Component({
  selector: 'app-loading-spinner',
  templateUrl: './loading-spinner.component.html',
  styleUrls: ['./loading-spinner.component.scss']
})
export class LoadingSpinnerComponent implements OnInit,DoCheck {
  @Input("loading") loading: boolean = true;
  @Input("message") message: string = '';
  @ViewChild('spinnerRef') private spinnerRef: TemplateRef<any>;

  config:any ={};
  private overlay: OverlayRef;

  constructor(private vcRef: ViewContainerRef, private overlayService: OverlayService) { }

  ngOnInit(): void {
    this.config = {
      hasBackdrop: true,
      positionStrategy: this.overlayService.positionGloballyCenter()
    };

    this.overlay = this.overlayService.createOverlay(this.config);
  }

  ngDoCheck(): void {
    if (this.loading && !this.overlay.hasAttached() && this.spinnerRef != undefined) {
      this.overlayService.attachTemplatePortal(this.overlay, this.spinnerRef, this.vcRef);
    } else if (!this.loading && this.overlay.hasAttached()) {
      this.overlay.detach();
    }
  }
}

Here is my code for my component. Now I removed many of the inputs, and broke this down to just what I needed. An input to indicate if we are still loading, and the message to indicate what we are doing.

.center-content {
    display:flex;
    justify-content:center;
    align-items:center;
}

This is my scss to center the content in my HTML.

<ng-template #spinnerRef>
    <mat-card>
        <mat-card-header>
            <mat-card-title>
                {{message}}
            </mat-card-title>
        </mat-card-header>
        <mat-card-content class="center-content">
            <mat-spinner [color]="'warn'">
            </mat-spinner>
        </mat-card-content>
    </mat-card>
</ng-template>

<ng-content></ng-content>

This will allow the user to see the page as it is built, but keep them from interacting with it until it has completed loading, while showing a nicely formatted mat-card with a spinner that the application is loading.

<app-loading-spinner [loading]="true" message="Loading Data">
    <p>Waiting for data to load</p>
</app-loading-spinner>

Simple usage your wrapped HTML will be loaded, and a Spinner with message “Loading Data”

By Jeffery Miller

I am known for being able to quickly decipher difficult problems to assist development teams in producing a solution. I have been called upon to be the Team Lead for multiple large-scale projects. I have a keen interest in learning new technologies, always ready for a new challenge.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.