Last active
March 25, 2020 17:36
-
-
Save wottpal/3596b03be5e9d533a08fc4fe792bb9f7 to your computer and use it in GitHub Desktop.
Simple Angular Counter/Countdown Component with dayjs & rxjs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* | |
* Example Usage: | |
* | |
* # Counter | |
* const now = dayjs() | |
* <app-counter [autostart]="true" [mode]="forwards" [startTime]="now"></app-counter> | |
* | |
* # Countdown | |
* const later = dayjs().add(1, 'minute') | |
* <app-counter [autostart]="true" [mode]="backwards" [stopTime]="later"></app-counter> | |
* | |
*/ | |
import { Component, Input, OnDestroy, OnInit } from '@angular/core'; | |
import * as dayjs from 'dayjs'; | |
import { interval, Subject, Subscription } from 'rxjs'; | |
import { takeUntil } from 'rxjs/operators'; | |
@Component({ | |
selector: 'app-counter', | |
template: `{{ timeFormatted }}`, | |
}) | |
export class CounterComponent implements OnInit, OnDestroy { | |
// Forwards-Mode (Counter) | |
@Input() forwards: boolean | |
@Input() startTime: Date | |
public elapsedSeconds: number | |
public counterHasStarted: boolean | |
// Backwards-Mode (Countdown) | |
@Input() backwards: boolean | |
@Input() stopTime: Date | |
public countdownHasFinished: boolean | |
public remainingSeconds: number | |
public timeFormatted: string | |
@Input() autostart: boolean | |
@Input() updateInterval: number = 1000 / 2 | |
public timeInterval$: Subscription | |
private unsubscribe$ = new Subject() | |
constructor() { } | |
ngOnInit() { | |
if (this.autostart) { | |
this.start() | |
} | |
} | |
ngOnDestroy() { | |
this.stop() | |
} | |
/** | |
* Initializes the Counter / Countdown | |
*/ | |
public start() { | |
if (this.forwards == this.backwards) { | |
console.error("Couldn't start counter as no mode or both modes are set.") | |
return | |
} | |
if (this.forwards && (!this.startTime || !dayjs(this.startTime).isValid())) { | |
console.error("Couldn't start counter as mode is 'forwards' but no start-time is provided.") | |
return | |
} | |
if (this.backwards && (!this.stopTime || !dayjs(this.stopTime).isValid())) { | |
console.error("Couldn't start counter as mode is 'forwards' but no start-time is provided.") | |
return | |
} | |
// Start Interval | |
this.timeInterval$ = interval(this.updateInterval).startWith(0).pipe( | |
takeUntil(this.unsubscribe$) | |
).subscribe(_ => { | |
this.updateTime() | |
}) | |
} | |
/** | |
* Stops the Counter / Countdown | |
*/ | |
public stop() { | |
this.unsubscribe$.next(true) | |
this.unsubscribe$.complete() | |
} | |
/** | |
* Updates `timeFormatted` of the Counter / Countdown | |
*/ | |
private updateTime() { | |
const now = dayjs() | |
if (this.forwards) { | |
// Start-Time from which the counter gets increased | |
const startTime = dayjs(this.startTime) | |
this.counterHasStarted = now.isAfter(startTime) | |
if (!this.counterHasStarted) { | |
this.timeFormatted = '0:00' | |
this.elapsedSeconds = 0 | |
return | |
} | |
let elapsedTime = dayjs(now.valueOf() - startTime.valueOf()) | |
elapsedTime = elapsedTime.subtract(dayjs().utcOffset(), 'minute') | |
this.elapsedSeconds = now.diff(startTime, 'second') | |
const format = elapsedTime.hour() ? 'H:mm:ss' : 'm:ss' | |
this.timeFormatted = elapsedTime.format(format) | |
} else if (this.backwards) { | |
// Stop-Time until which the countdown gets decreased | |
const stopTime = dayjs(this.stopTime) | |
this.countdownHasFinished = now.isAfter(stopTime) | |
if (this.countdownHasFinished) { | |
this.timeFormatted = '0:00' | |
this.remainingSeconds = 0 | |
return | |
} | |
let remainingTime = dayjs(stopTime.valueOf() - now.valueOf()) | |
remainingTime = remainingTime.subtract(dayjs().utcOffset(), 'minute') | |
this.remainingSeconds = stopTime.diff(now, 'second') | |
const format = remainingTime.hour() ? 'H:mm:ss' : 'm:ss' | |
this.timeFormatted = remainingTime.format(format) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment