在管道中实现倒数计时器,并在计时器结束时使用附加服务更新组件
Implementing a countdown timer in a pipe with additional service to update component when timer finishes
我正在使用我的 Angular 应用程序在屏幕上显示 date/time 倒计时。这是从特定日期和时间开始的倒计时。
运行 它每一秒都在用 Google Chrome 杀死我的 CPU。
可能是 moment() 库有一些效果,但还不确定。
我可能会马上尝试一下,看看是否有帮助。
是否有更好(更有效)的方法来做到这一点?
这是我的代码
calculateTimeRemaining(endDate: Date, timeZone: string, eventId: number) {
setInterval(() => {
const element = document.getElementById("event-" + eventId);
if (element) {
const currentDateUtc = moment().utc();
const endDateUtc = moment(endDate).utc(true);
endDateUtc.tz(timeZone);
const dif = moment.duration(endDateUtc.diff(currentDateUtc));
const hours = dif.hours();
const minutes = dif.minutes();
const seconds = dif.seconds();
if (hours < 1) {
element.innerHTML = minutes + ' m ' + seconds + ' s';
} else {
element.innerHTML = hours + ' h ' + minutes + ' m ' + seconds + ' s';
}
}
}, 1000);
}
<span id="event-{{event.id}}" class="float-right yb-color">{{calculateTimeRemaining(event.datePendingUtc, event.yogabandTimeZoneId, event.id)}}</span>
编辑(解决方案) - 我已经在管道中实现了倒数计时器(按照推荐)来解决这个问题。我将在下面包含管道的完整代码以及我如何调用它。我还有一项服务,用于在计时器启动时向我的组件发送通知,以便我可以执行其他操作。
import { Pipe, PipeTransform } from '@angular/core';
import { Observable, of, timer } from 'rxjs';
import { map, takeWhile } from 'rxjs/operators';
import { EventService } from 'src/app/core/services/event.service';
@Pipe({
name: 'timeRemaining'
})
export class TimeRemainingPipe implements PipeTransform {
eventId: number;
expired = false;
constructor(private eventService: EventService) {}
/**
* @param futureDate should be in a valid Date Time format
* e.g. YYYY-MM-DDTHH:mm:ss.msz
* e.g. 2021-10-06T17:27:10.740z
*/
public transform(futureDateUtc: string, eventId: number): Observable<string> {
this.eventId = eventId;
/**
* Initial check to see if time remaining is in the future
* If not, don't bother creating an observable
*/
if (!futureDateUtc || this.getMsDiff(futureDateUtc) < 0) {
console.info('Pipe - Time Expired Event: ' + eventId);
return of('EXPIRED');
}
return timer(0, 1000).pipe(
takeWhile(() => !this.expired),
map(() => {
return this.msToTime(this.getMsDiff(futureDateUtc));
})
);
}
/**
* Gets the millisecond difference between a future date and now
* @private
* @param futureDateUtc: string
* @returns number milliseconds remaining
*/
// Z converts to local time
private getMsDiff = (futureDate: string): number => (+(new Date(futureDate + 'Z')) - Date.now());
/**
* Converts milliseconds to the
*
* @private
* @param msRemaining
* @returns null when no time is remaining
* string in the format `HH:mm:ss`
*/
private msToTime(msRemaining: number): string | null {
if (msRemaining < 0) {
console.info('Pipe - No Time Remaining:', msRemaining);
this.expired = true;
this.eventService.expired(this.eventId);
return 'EXPIRED';
}
let seconds: string | number = Math.floor((msRemaining / 1000) % 60),
minutes: string | number = Math.floor((msRemaining / (1000 * 60)) % 60),
hours: string | number = Math.floor((msRemaining / (1000 * 60 * 60)) % 24);
/**
* Add the relevant `0` prefix if any of the numbers are less than 10
* i.e. 5 -> 05
*/
seconds = (seconds < 10) ? '0' + seconds : seconds;
minutes = (minutes < 10) ? '0' + minutes : minutes;
hours = (hours < 10) ? '0' + hours : hours;
return `${hours}:${minutes}:${seconds}`;
}
}
<span class="float-right yb-color">{{(event.dateUtc | timeRemaining: event.id | async}}</span>
这是服务
export class EventService {
private subject = new Subject <any> ();
expired(eventId: number) {
this.subject.next(eventId);
}
expiredEvents(): Observable <any> {
return this.subject.asObservable();
}
}
// how it's called in the component
this.eventService.expiredEvents().subscribe(eventId => {
console.info('Service - Expired Event:', eventId);
// do something now w/ expired event
});
为了性能,不要在 Angular 模板中调用任何函数,因为它会在每次更改检测中 运行。
这篇文章很好地描述了这个问题https://medium.com/showpad-engineering/why-you-should-never-use-function-calls-in-angular-template-expressions-e1a50f9c0496
我们可以使用纯管道更改您的实现
time-remaining.pipe.ts
@Pipe({
name: 'timeRemaining',
})
export class TimeRemainingPipe {
transform(event: any): any {
// put implementation here
// return time remaining
}
}
我相信管道,你可以删除 const element = document.getElementById("event-" + eventId);
部分。
component.html
<span id="event-{{event.id}}" class="float-right yb-color">{{ event | timeRemaining }}</span>
我正在使用我的 Angular 应用程序在屏幕上显示 date/time 倒计时。这是从特定日期和时间开始的倒计时。
运行 它每一秒都在用 Google Chrome 杀死我的 CPU。
可能是 moment() 库有一些效果,但还不确定。
我可能会马上尝试一下,看看是否有帮助。
是否有更好(更有效)的方法来做到这一点?
这是我的代码
calculateTimeRemaining(endDate: Date, timeZone: string, eventId: number) {
setInterval(() => {
const element = document.getElementById("event-" + eventId);
if (element) {
const currentDateUtc = moment().utc();
const endDateUtc = moment(endDate).utc(true);
endDateUtc.tz(timeZone);
const dif = moment.duration(endDateUtc.diff(currentDateUtc));
const hours = dif.hours();
const minutes = dif.minutes();
const seconds = dif.seconds();
if (hours < 1) {
element.innerHTML = minutes + ' m ' + seconds + ' s';
} else {
element.innerHTML = hours + ' h ' + minutes + ' m ' + seconds + ' s';
}
}
}, 1000);
}
<span id="event-{{event.id}}" class="float-right yb-color">{{calculateTimeRemaining(event.datePendingUtc, event.yogabandTimeZoneId, event.id)}}</span>
编辑(解决方案) - 我已经在管道中实现了倒数计时器(按照推荐)来解决这个问题。我将在下面包含管道的完整代码以及我如何调用它。我还有一项服务,用于在计时器启动时向我的组件发送通知,以便我可以执行其他操作。
import { Pipe, PipeTransform } from '@angular/core';
import { Observable, of, timer } from 'rxjs';
import { map, takeWhile } from 'rxjs/operators';
import { EventService } from 'src/app/core/services/event.service';
@Pipe({
name: 'timeRemaining'
})
export class TimeRemainingPipe implements PipeTransform {
eventId: number;
expired = false;
constructor(private eventService: EventService) {}
/**
* @param futureDate should be in a valid Date Time format
* e.g. YYYY-MM-DDTHH:mm:ss.msz
* e.g. 2021-10-06T17:27:10.740z
*/
public transform(futureDateUtc: string, eventId: number): Observable<string> {
this.eventId = eventId;
/**
* Initial check to see if time remaining is in the future
* If not, don't bother creating an observable
*/
if (!futureDateUtc || this.getMsDiff(futureDateUtc) < 0) {
console.info('Pipe - Time Expired Event: ' + eventId);
return of('EXPIRED');
}
return timer(0, 1000).pipe(
takeWhile(() => !this.expired),
map(() => {
return this.msToTime(this.getMsDiff(futureDateUtc));
})
);
}
/**
* Gets the millisecond difference between a future date and now
* @private
* @param futureDateUtc: string
* @returns number milliseconds remaining
*/
// Z converts to local time
private getMsDiff = (futureDate: string): number => (+(new Date(futureDate + 'Z')) - Date.now());
/**
* Converts milliseconds to the
*
* @private
* @param msRemaining
* @returns null when no time is remaining
* string in the format `HH:mm:ss`
*/
private msToTime(msRemaining: number): string | null {
if (msRemaining < 0) {
console.info('Pipe - No Time Remaining:', msRemaining);
this.expired = true;
this.eventService.expired(this.eventId);
return 'EXPIRED';
}
let seconds: string | number = Math.floor((msRemaining / 1000) % 60),
minutes: string | number = Math.floor((msRemaining / (1000 * 60)) % 60),
hours: string | number = Math.floor((msRemaining / (1000 * 60 * 60)) % 24);
/**
* Add the relevant `0` prefix if any of the numbers are less than 10
* i.e. 5 -> 05
*/
seconds = (seconds < 10) ? '0' + seconds : seconds;
minutes = (minutes < 10) ? '0' + minutes : minutes;
hours = (hours < 10) ? '0' + hours : hours;
return `${hours}:${minutes}:${seconds}`;
}
}
<span class="float-right yb-color">{{(event.dateUtc | timeRemaining: event.id | async}}</span>
这是服务
export class EventService {
private subject = new Subject <any> ();
expired(eventId: number) {
this.subject.next(eventId);
}
expiredEvents(): Observable <any> {
return this.subject.asObservable();
}
}
// how it's called in the component
this.eventService.expiredEvents().subscribe(eventId => {
console.info('Service - Expired Event:', eventId);
// do something now w/ expired event
});
为了性能,不要在 Angular 模板中调用任何函数,因为它会在每次更改检测中 运行。
这篇文章很好地描述了这个问题https://medium.com/showpad-engineering/why-you-should-never-use-function-calls-in-angular-template-expressions-e1a50f9c0496
我们可以使用纯管道更改您的实现
time-remaining.pipe.ts
@Pipe({
name: 'timeRemaining',
})
export class TimeRemainingPipe {
transform(event: any): any {
// put implementation here
// return time remaining
}
}
我相信管道,你可以删除 const element = document.getElementById("event-" + eventId);
部分。
component.html
<span id="event-{{event.id}}" class="float-right yb-color">{{ event | timeRemaining }}</span>