angular2你如何在`ngFor`中渲染一个计时器组件
Posted
技术标签:
【中文标题】angular2你如何在`ngFor`中渲染一个计时器组件【英文标题】:angular2 how do you render a timer Component inside `ngFor` 【发布时间】:2017-06-27 12:17:01 【问题描述】:我想呈现一个视觉倒数计时器。我正在使用这个组件,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
);
【问题讨论】:
【参考方案1】:首先可疑的是toJSON
(名称错误或返回string
或将字符串转换为数组),timers
数组中包含哪些对象?
在更改检测期间,可能会多次评估循环,因此它不应生成新对象。此外,toJSON
管道应标记为 pure
(请参阅管道装饰器选项)。此外,最好为ngFor
提供trackBy
函数。一般来说,在这种情况下更新类字段而不是使用管道会更好。
public snapshots:any[] = [];
private timers:any[] = [];
setInterval(()=>
this.snapshots = this.updateSnapshots(this.timers);
, 100);
<ion-card class="timer"
*ngFor="let snapshot of snapshots"
>
【讨论】:
toJSON
可以很容易地重命名为snapshot
,它在几秒钟内返回一个带有snapshot.remaining
的简单对象,具有不同的精度。我添加了trackByFn
,但这并没有解决问题。 pure:true
没有帮助,因为 SimpleChange
需要对象引用保持 SAME 才能捕获等于 snapshot.remaining
的 changes.current.previousValue
。问题是计时器在ngFor
循环期间在MS
中滴答作响。当我输入这个时,我在想一个黑客可能是去抖动snaphshot
...
我的意思是throttle
,我尝试了throttle
不同的值,但没有运气。此外,ngFor
循环有多快并不重要,因为正如您所提到的,更改检测循环似乎多次调用ngFor
。如果计时值发生变化,则会引发错误。即使精度为 1 秒也是如此。
如果我从ngFor
中取出计时器,我可以每 10 毫秒渲染一次计时器而不会出错。更新问题以反映这一事实
我认为在这种情况下你应该在模板所有者组件中运行setInterval
,并在回调中更新快照数组,换句话说,推送新值而不是让角度随机拉取新值。跨度>
行得通!!!它没有被完全封装,但它似乎确实有效。我可以使用ngFor
,并且仍然可以通过这种方法渲染百分之一秒。我将稍等片刻,看看是否有另一种方法可以更好地用 Timer 封装——但如果没有,我会接受你的回答。以上是关于angular2你如何在`ngFor`中渲染一个计时器组件的主要内容,如果未能解决你的问题,请参考以下文章
Angular2:输入控制带有ngFor的表单标签内的损失值
Angular 2 *ngFor的数组/ json渲染中的深层嵌套json到数组[重复]