Spinner for SLDS-UI Module



One more interesting use case for SLDS UI Module I want to describe is Global Spinner. I'm not sure is closing of the whole page under the Spinner on requests is good practice, but my practice shows it looks nice and is clear for users that something happens in background and they should wait.

As Global Spinner should be the singleton we need to provide some interface to interact with this component from any place in our application.

First generate component (like described here - Custom SLDS-UI Angular Module)

ng g c slds-global-spinner --module=slds-ui.module.ts --prefix="" --export=true

and service

ng g s slds-global-spinner 

(move slds-global-spinner service to the component folder to make it looks better)

Add SldsGlobalSpinnerService to "providers" list in slds-iu.module.ts

slds-global-spinner.service.ts
import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';

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

    level = 0;

    private showChangeSource = new Subject<boolean>();

    getShow(): Observable<boolean> {
        return this.showChangeSource.asObservable();
    }

    showSpinner(): void {
        if (this.level === 0) {
            this.showChangeSource.next(true);
        }
        this.level++;
    }

    hideSpinner(): void {
        this.level--;
        if (this.level === 0) {
            this.showChangeSource.next(false);
        }
    }

}

slds-global-spinner.component.ts
import { Component, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { SldsGlobalSpinnerService } from './slds-global-spinner.service';

@Component({
    selector: 'slds-global-spinner',
    templateUrl: './slds-global-spinner.component.html',
    styleUrls: ['./slds-global-spinner.component.scss']
})
export class SldsGlobalSpinnerComponent implements OnDestroy {

    show = false;
    private showChangeSubscription: Subscription;

    constructor(
        private spinnerService: SldsGlobalSpinnerService
    ) {
        this.showChangeSubscription = this.spinnerService.getShow().subscribe(
            (val) => {
            this.show = val;
            }
        );
    }

    ngOnDestroy() {
        this.showChangeSubscription.unsubscribe();
    }

}

slds-global-spinner.component.html
<div *ngIf="show" class="c-spinner_container">
    <div class="c-spinner c-spinner-1"></div>
</div>

slds-global-spinner.service.scss
.c-spinner_container {
    position: fixed;
    top: 0;
    right: 0;
    left: 0;
    bottom: 0;
    z-index: 99999;
}
.c-spinner {
    position: relative;
    display: inline-block;
    top: 50%;
    left: 50%;
    margin-top: -50px;
    margin-left: -50px;
    width: 100px;
    height: 100px;
    border: 2px solid #01bcd4;
    border-radius: 50%;
    animation: spin 0.75s infinite linear;
}
.c-spinner-1 {
    border-top-width: 0;
}
.c-spinner::before,
.c-spinner::after {
    left: -2px;
    top: -2px;
    display: none;
    position: absolute;
    content: '';
    width: inherit;
    height: inherit;
    border: inherit;
    border-radius: inherit;
}
@keyframes spin {
    from {
        transform: rotate(0deg);
    }
    to {
        transform: rotate(360deg);
    }
}

To make it work add this tags <slds-global-spinner></slds-global-spinner> to the end of app.component.html

Usage example in task-details.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { RemoteService } from '../services/remote.service';
import { ITask } from '../interfaces';
import { TaskService } from '../services/task.service';
import { SldsGlobalSpinnerService } from '../slds-ui/slds-global-spinner/slds-global-spinner.service';

@Component({
    selector: 'app-task-details',
    templateUrl: './task-details.component.html',
    styleUrls: ['./task-details.component.scss']
})
export class TaskDetailsComponent implements OnInit {

    task: ITask = null;

    constructor(
        private route: ActivatedRoute,
        private router: Router,
        private taskService: TaskService,
        private spinnerService: SldsGlobalSpinnerService
    ) {}

    ngOnInit() {
        const taskId = this.route.snapshot.paramMap.get('id');
        console.log('TASK ID:', taskId);
        this.getTask(taskId);
    }

    async getTask(id: string) {
        this.spinnerService.showSpinner();
        try {
            this.task = await this.taskService.getTask(id);
        } catch (err) {
            console.log('ERR:', err);
            if (+err.status === 404) {
                this.spinnerService.hideSpinner();
                this.router.navigate(['/tasks']);
                return;
            }
            throw err;
        }
        this.spinnerService.hideSpinner();
    }

    ...

}

Also you can see in this example new approach to work with promises - async/await. 

Now, when your remove requests start you should see this nice spinner in your page

Angular SLDS Global Spinner








Comments

Popular posts from this blog

HTTPS in local environment for Angular + Flask project.

Salesforce Authentication 2 (Token Validation and Refresh)

User authentification with Flask-Login