angular2如何在`ngFor`中渲染一个计时器组件

时间:2017-02-09 13:04:37

标签: angular ionic2 zonejs

我想渲染一个视觉倒数计时器。我正在使用此组件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
  )

更新 (经过大量测试后,我发现问题并不是我渲染时的精确度,而且这个问题已经改变以反映出来。)

问题是在1个变化检测循环内多次调用ngFor。无论我的时间间隔或精确度snapshot.remaining(即秒或十分之一秒),如果snapshot.remaining在更改检测期间随后调用ngFor时发生更改,我会收到异常:

Expression has changed after it was checked

如果我在不使用ngFor的情况下仅渲染单个计时器,则更改检测工作正常 - 即使是10ms的间隔。

如何在页面上渲染多个计时器,大概使用ngFor,而不会触发此异常?

解决方案

经过一些测试后,似乎问题是使用SnapshotPipengFor来捕获Timer数据的快照。最终工作的是在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 个答案:

答案 0 :(得分:2)

第一个可疑的事情是toJSON(名称错误或返回string或将字符串转换为数组),哪些对象包含在timers数组中?

在更改检测期间,可能会多次评估循环,因此不应生成新对象。另外toJSON管道应标记为pure(请参阅管道装饰器选项)。此外,最好为trackBy提供ngFor功能。通常,在这种情况下更好的是更新类字段而不是使用管道。

public snapshots:any[] = [];

private timers:any[] = [];

setInterval(()=>{
    this.snapshots = this.updateSnapshots(this.timers);
}, 100);

<ion-card  class="timer"
  *ngFor="let snapshot of snapshots"
>