2017-02-09 5 views
4

Я хочу визуализировать визуальный таймер обратного отсчета. Я использую этот компонент, https://github.com/crisbeto/angular-svg-round-progressbar, который полагается на SimpleChange для доставки changes.current.previousValue & changes.current.currentValue.angular2 как визуализировать таймер Компонент внутри `ngFor`

Вот код шаблона

<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, чтобы захватить снимок данных таймера. Что в конечном итоге работало, так это взять 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 
      ); 
     } 
    } 

ответ

2

Первая подозрительная вещь toJSON (он имеет неправильное имя или возвращает string или преобразует строку в массив), какие объекты содержатся в timers массиве?

Во время обнаружения изменений цикл может быть оценен несколько раз, поэтому он не должен генерировать новые объекты. Также toJSON труба должна быть обозначена как pure (см. Опции декоратора труб). Также лучше обеспечить функцию trackBy для ngFor. В общем случае лучше в этом случае обновить поле класса вместо использования pipe.


public snapshots:any[] = []; 

private timers:any[] = []; 

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

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

'toJSON' может быть переименован легко' snapshot', он возвращает простой объект с 'snapshot.remaining' в секундах с разным уровнем точности. Я добавил «trackByFn», но это не решило проблему. 'pure: true' не помогает, потому что' SimpleChange' нуждается в ссылке на объект, чтобы оставаться ** SAME **, чтобы записать 'changes.current.previousValue', что равно' snapshot.remaining'. Проблема в том, что таймер находится в 'MS' во время цикла' ngFor'.Когда я печатаю это, я думаю, что взлом может состоять в том, чтобы отбросить «snaphshot» ... – michael

+0

Я имею в виду «throttle», и я пробовал «дроссель» с разными значениями, но не повезло. Кроме того, не имеет значения, насколько быстро существует цикл 'ngFor', поскольку, как вы уже упоминали, цикл обнаружения изменений, по-видимому, несколько раз вызывает' ngFor'. если значение синхронизации изменяется, ошибка возникает. Это верно, даже если точность составляет 1 секунду. – michael

+0

Если я возьму таймер из 'ngFor', я смогу отображать таймер каждые 10MS без ошибок. Обновление вопроса, чтобы отразить этот факт – michael

Смежные вопросы