angular2 你如何在`ngFor`中渲染一个计时器组件
angular2 how do you render a timer Component inside `ngFor`
我想渲染一个视觉倒数计时器。我正在使用此组件 https://github.com/crisbeto/angular-svg-round-progressbar,它依赖于 SimpleChange
来交付 changes.current.previousValue
和 changes.current.currentValue
。
这是模板代码
<ion-card class="timer"
*ngFor="let snapshot of timers | snapshot"
>
<ion-card-content>
<round-progress
[current]="snapshot.remaining"
[max]="snapshot.duration"
[rounded]="true"
[responsive]="true"
[duration]="800"
>
</round-progress>
</ion-card>
我正在使用此代码触发 angular2
变化检测
this._tickIntervalHandler = setInterval( ()=>{
// run change detection
return;
}
// interval = 1000ms
, interval
)
已更新
(经过多次测试,我发现问题不在于我渲染的时间精度,并且已更改此问题以反映这一点。)
问题是 ngFor
在 1 个变化检测循环中被多次调用。无论我的滴答间隔或 snapshot.remaining
的精度如何(即秒或十分之一秒),如果 snapshot.remaining
在更改检测期间在随后对 ngFor
的调用中发生更改,我会得到一个异常:
Expression has changed after it was checked
如果我在不使用 ngFor
的情况下只渲染一个计时器,那么变化检测工作正常——即使是 10ms
.
的间隔
如何在一个页面上呈现多个计时器,大概使用 ngFor
,而不触发此异常?
解决方案
经过一些测试,问题似乎是使用 SnapshotPipe
和 ngFor
来捕获定时器数据的快照。最终起作用的是在View Component中取Timer数据的snapshot
。正如下面的答案中提到的,这使用 pull
方法来获取更改,而不是 push
方法。
// timers.ts
export class TimerPage {
// use with ngFor
snapshots: Array<any> = [];
constructor(timerSvc: TimerSvc){
let self = this;
timerSvc.setIntervalCallback = function(){
self.snapshots = self.timers.map( (timer)=>timer.getSnapshot() );
}
}
}
// timer.html
<ion-card class="timer" *ngFor="let snapshot of snapshots">
<ion-card-content>
<round-progress
[current]="snapshot.remaining"
[max]="snapshot.duration"
[rounded]="true"
[responsive]="true"
[duration]="800"
>
</round-progress>
</ion-card>
// TimerSvc can start the tickInterval
export class TimerSvc {
_tickIntervalCallback: ()=>void;
_tickIntervalHandler: number;
setIntervalCallback( cb: ()=>void) {
this._tickIntervalCallback = cb;
}
startTicking(interval:number=100){
this._tickIntervalHandler = setInterval(
this._tickIntervalCallback
, interval
);
}
}
第一个可疑的是toJSON
(它有错误的名称或returns string
或将字符串转换为数组),timers
数组中包含哪些对象?
在更改检测期间,循环可能会被评估多次,因此它不应生成新对象。此外 toJSON
管道应标记为 pure
(请参阅管道装饰器选项)。另外最好为ngFor
提供trackBy
功能。一般来说,在这种情况下最好更新 class 字段而不是使用管道。
public snapshots:any[] = [];
private timers:any[] = [];
setInterval(()=>{
this.snapshots = this.updateSnapshots(this.timers);
}, 100);
<ion-card class="timer"
*ngFor="let snapshot of snapshots"
>
我想渲染一个视觉倒数计时器。我正在使用此组件 https://github.com/crisbeto/angular-svg-round-progressbar,它依赖于 SimpleChange
来交付 changes.current.previousValue
和 changes.current.currentValue
。
这是模板代码
<ion-card class="timer"
*ngFor="let snapshot of timers | snapshot"
>
<ion-card-content>
<round-progress
[current]="snapshot.remaining"
[max]="snapshot.duration"
[rounded]="true"
[responsive]="true"
[duration]="800"
>
</round-progress>
</ion-card>
我正在使用此代码触发 angular2
变化检测
this._tickIntervalHandler = setInterval( ()=>{
// run change detection
return;
}
// interval = 1000ms
, interval
)
已更新 (经过多次测试,我发现问题不在于我渲染的时间精度,并且已更改此问题以反映这一点。)
问题是 ngFor
在 1 个变化检测循环中被多次调用。无论我的滴答间隔或 snapshot.remaining
的精度如何(即秒或十分之一秒),如果 snapshot.remaining
在更改检测期间在随后对 ngFor
的调用中发生更改,我会得到一个异常:
Expression has changed after it was checked
如果我在不使用 ngFor
的情况下只渲染一个计时器,那么变化检测工作正常——即使是 10ms
.
如何在一个页面上呈现多个计时器,大概使用 ngFor
,而不触发此异常?
解决方案
经过一些测试,问题似乎是使用 SnapshotPipe
和 ngFor
来捕获定时器数据的快照。最终起作用的是在View Component中取Timer数据的snapshot
。正如下面的答案中提到的,这使用 pull
方法来获取更改,而不是 push
方法。
// timers.ts
export class TimerPage {
// use with ngFor
snapshots: Array<any> = [];
constructor(timerSvc: TimerSvc){
let self = this;
timerSvc.setIntervalCallback = function(){
self.snapshots = self.timers.map( (timer)=>timer.getSnapshot() );
}
}
}
// timer.html
<ion-card class="timer" *ngFor="let snapshot of snapshots">
<ion-card-content>
<round-progress
[current]="snapshot.remaining"
[max]="snapshot.duration"
[rounded]="true"
[responsive]="true"
[duration]="800"
>
</round-progress>
</ion-card>
// TimerSvc can start the tickInterval
export class TimerSvc {
_tickIntervalCallback: ()=>void;
_tickIntervalHandler: number;
setIntervalCallback( cb: ()=>void) {
this._tickIntervalCallback = cb;
}
startTicking(interval:number=100){
this._tickIntervalHandler = setInterval(
this._tickIntervalCallback
, interval
);
}
}
第一个可疑的是toJSON
(它有错误的名称或returns string
或将字符串转换为数组),timers
数组中包含哪些对象?
在更改检测期间,循环可能会被评估多次,因此它不应生成新对象。此外 toJSON
管道应标记为 pure
(请参阅管道装饰器选项)。另外最好为ngFor
提供trackBy
功能。一般来说,在这种情况下最好更新 class 字段而不是使用管道。
public snapshots:any[] = [];
private timers:any[] = [];
setInterval(()=>{
this.snapshots = this.updateSnapshots(this.timers);
}, 100);
<ion-card class="timer"
*ngFor="let snapshot of snapshots"
>