Animations don't work when triggered by Events published from a provider module


#1

I have a page with this code:

import { Component } from '@angular/core';
import { animate, state, style, transition, trigger } from '@angular/animations';
import { NavController } from 'ionic-angular';
import { Events } from 'ionic-angular';

@Component({
  selector: 'page-home',
  animations: [
    trigger('colorChange', [
      state('black', style({ background: 'black' })),
      transition('* => black', animate('100ms')),
      state('red', style({ background: 'red' })),
      transition('* => red', animate('100ms')),
      state('green', style({ background: 'green' })),
      transition('* => green', animate('100ms')),
      state('blue', style({ background: 'blue' })),
      transition('* => blue', animate('100ms')),
      state('yellow', style({ background: 'yellow' })),
      transition('* => yellow', animate('100ms')),
      state('orange', style({ background: 'orange' })),
      transition('* => orange', animate('100ms')),
      state('aqua', style({ background: 'aqua' })),
      transition('* => aqua', animate('100ms')),
      state('white', style({ background: 'white' })),
      transition('* => white', animate('100ms')),
      state('pink', style({ background: 'pink' })),
      transition('* => pink', animate('100ms'))
    ])
  ],
  //templateUrl: 'home.html'
  template: `
  <ion-header>
    <ion-navbar>
      <button ion-button menuToggle>
        <ion-icon name="menu"></ion-icon>
      </button>
      <ion-title>Home</ion-title>
    </ion-navbar>
  </ion-header>

  <ion-content padding [@colorChange]="currentColor">
    <button ion-button (click)="doColor(30)">Color Change</button>
  </ion-content>
`
})
export class HomePage {
  private colorCount: number = 0;
  private currentColor: string;
  private intervalID: any;

  colorArr: string[] = ['black', 'red', 'green', 'blue', 'yellow', 'orange', 'aqua', 'pink', 'white'];

  constructor(public navCtrl: NavController, public events: Events) {
    firebase.init();
    events.subscribe('doColorChange', args => {
      this.doColor(args);
    });
  //  events.publish('doColorChange', 30);
  }

  doColor(rate) {
    if (rate) {
      let interval: number = Math.round(1000 / (rate / 60) / 2); // 1000=mssec rate=BPM 60=secs/min /2= on/off per interval
      clearInterval(this.intervalID);
      this.intervalID = setInterval(() => {
        console.log('interval');
        this.changeColor();
      }, interval);
    } else {
      clearInterval(this.intervalID);
      this.currentColor = 'white';
    }
  }

  changeColor() {
    this.colorCount++;
    if (this.colorCount < this.colorArr.length) {
      this.currentColor = this.colorArr[this.colorCount];
    } else {
      this.colorCount = 0;
      this.currentColor = this.colorArr[0];
    }
  }
}

If I uncomment // events.publish('doColorChange', 30); , everything works as written. The page animates when doColor() is called. If doColorChange at line 55 is triggered from a subscribed event in a provider module, which is initialized in platform.ready(), everything still fires, this.changeColor() is called inside the setInterval,and changeColor() runs, but the animations don’t fire.


#2

I’m leaving this up here because animations are tricky so someone may learn from it, but I found that I needed to wrap my call to doColor in an ngZone.run(). I don’t understand Angular 4 digesting yet, but events subscribed to from a provider need to be handled inside a zone.run