Angular 2 Spinner Component
As I posted the little Input Debounce Component some days ago, here is a very little Angular 2 Spinner Component. The component uses a spinner from Tobias Ahlin’s SpinKit. For completeness, I’ll post the CSS and HTML code for it here, too.
Component Code
CSS
.spinner {
width: 40px;
height: 40px;
position: relative;
margin: 100px auto;
}
.double-bounce1, .double-bounce2 {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: #333;
opacity: 0.6;
position: absolute;
top: 0;
left: 0;
-webkit-animation: sk-bounce 2.0s infinite ease-in-out;
animation: sk-bounce 2.0s infinite ease-in-out;
}
.double-bounce2 {
-webkit-animation-delay: -1.0s;
animation-delay: -1.0s;
}
@-webkit-keyframes sk-bounce {
0%, 100% { -webkit-transform: scale(0.0) }
50% { -webkit-transform: scale(1.0) }
}
@keyframes sk-bounce {
0%, 100% {
transform: scale(0.0);
-webkit-transform: scale(0.0);
} 50% {
transform: scale(1.0);
-webkit-transform: scale(1.0);
}
}
HTML
<div [hidden]="!isDelayedRunning" class="spinner">
<div class="double-bounce1"></div>
<div class="double-bounce2"></div>
</div>
Angular 2 Component
'use strict';
import {Component, Input, OnDestroy} from 'angular2/core';
@Component({
selector: 'my-spinner',
templateUrl: 'app/components/spinner/spinner.html'
})
export class SpinnerComponent implements OnDestroy {
private currentTimeout: number;
private isDelayedRunning: boolean = false;
@Input()
public delay: number = 300;
@Input()
public set isRunning(value: boolean) {
if (!value) {
this.cancelTimeout();
this.isDelayedRunning = false;
return;
}
if (this.currentTimeout) {
return;
}
this.currentTimeout = setTimeout(() => {
this.isDelayedRunning = value;
this.cancelTimeout();
}, this.delay);
}
private cancelTimeout(): void {
clearTimeout(this.currentTimeout);
this.currentTimeout = undefined;
}
ngOnDestroy(): any {
this.cancelTimeout();
}
}
At first the necessary imports are done. Then a new component is created with an external template file. The contents of that file is the HTML code as shown above. The class SpinnerComponent
has two private fields:
currentTimeout
: This is the identifier for the set timeout, so it can be cancelled.isDelayedRunning
: This is the internal state of the spinner, if it is running or not.
And it has a public input field delay
representing the milliseconds to wait, before showing the spinner. A second input isRunning
is defined a setter, so we can execute some code, if the value gets set:
At first, it checks, if the actual value
is false
. If so, it cancels the timeout and immediately switches isDelayedRunning
to false
which leads to hiding the spinner right away. We don’t want to use setTimeout
when hiding the spinner, since it would not make any sense. The spinner would be shown even the data has been loaded already.
The other case, value
is set to true
, it first checks if a timeout has already been set to not start a new one. If not, we create a timeout with the given delay
. When it times out, isDelayedRunning
will be set to true
, showing the spinner.
I also implementedOnDestroy
to clean up any timeout, which could still be active, when the spinner gets destroyed.
Usage
Imagine we’re doing a HTTP request, where the spinner should show, when the requests takes a little more time:
'use strict';
import {Component} from 'angular2/core';
import {SpinnerComponent} from '../spinner/spinner';
import {ApiService} from '../../services/apiService';
@Component({
selector: 'my-sample-view',
directives: [SpinnerComponent],
template: '<my-spinner [isRunning]="isRequesting"></my-spinner>',
})
export class SampleViewComponent {
public isRequesting: boolean;
public items: Array<any>;
constructor(private apiService: ApiService) {
this.refresh();
}
public refresh(): void {
this.isRequesting = true;
this.apiService.sampleHttpGetRequest()
.subscribe(
result => this.items = result,
() => this.stopRefreshing(),
() => this.stopRefreshing());
}
private stopRefreshing() {
this.isRequesting = false;
}
}
Same here: Do the necessary imports at first. We need the SpinnerComponent
and some imaginary ApiService
, which will be a representative for doing a HTTP GET request. We define a new component SampleViewComponent
, using the SpinnerComponent
and binding its isRunning
input to a variable called isRequesting
. When ever isRequesting
changes, it will update the state of the spinner. Within the refresh()
-method, isRequesting
is set to true
, creating the internal timeout. If the apiService.sampleHttpGetRequests()
takes longer than 300 msec, the spinner will show up. After the Observable is done (or got an error), we use stopRefreshing()
to set isRequesting
to false
again, which will hide the spinner.