Search code examples
angularangularjs-directivelifecycle-hook

Implementing a directive in Angular 4 to continuously cycle through background colors on host


I'm learning angular 4 and would like to implement a directive that causes the host element's background colors to cycle through the 7 listed in an array. Ideally I would want it to be continuous. I don't know which life-cycle hooks I need to hook into. Here's what I have at the moment. Presently, it's not even visibly cycling through the 7 one time, with one-second intervals, as expected through the use of the SetTimeOut. I've commented out the While block as that just hangs the browser.

import {
  Directive,
  OnInit,
  HostBinding,
  Input
} from '@angular/core';


@Directive({
  selector: '[rainbowize]'
})
export class RainbowizeDirective {
  colors: Array<string>;

 @HostBinding('style.backgroundColor') bgColor: string;

  constructor() {
    this.colors = ['violet', 'indigo', 'blue', 'green', 'yellow', 'orange', 'red'];
 }

 ngOnInit(){
   let that = this;
   //while (true) {
      for (let i = 0; i < 7; i++) {
        console.log(that.colors[i]);
        setTimeout(function () {
          that.bgColor = that.colors[i];
        }, 1000)
      }
    //}
  }
}

html:

<h2 rainbowize >This is a raibowized paragraph</h2>

Solution

  • Here is how you can do it:

    ngOnInit() {
        let that = this;
        let counter = 0;
        let length = this.colors.length;
    
        upd();
    
        function upd() {
            that.bgColor = that.colors[counter % length];
            counter++;
    
            // some stopping condition here
            if (counter < 20) {
                setTimeout(upd, 1000);
            }
        }
    }
    

    The most important thing here is this line:

    that.colors[counter % length];
    

    I use modulo operator %, which returns remainder after the integer division. So it will return:

    0%7 = 0
    1%7 = 1
    2%7 = 2
    ...
    6%7 = 6
    7%7 = 0  <---- here the sequence starts from the beginning
    8%7 = 1
    

    This will run until the counter variable reaches Number.MAX_SAFE_INTEGER, which is 9007199254740991.

    Another simpler approach could be to just do the following:

        that.bgColor = that.colors[counter % length];
        counter++;
    
        if (counter === length) {
            counter = 0;
        }
    

    Or use a circular linked list.

    But I'm still not clear as to where my error lies? Is it because I have my code in the ngOnInit's main body as opposed to having it a function there?

    The problem with your code has nothing to do with Angular. It is that you're adding all callbacks to be executed within a second. And since they are all executed very fast, you only see the latest change to red. You could fix it like this:

      for (let i = 0; i < 7; i++) {
        console.log(that.colors[i]);
        setTimeout(function () {
          that.bgColor = that.colors[i];
        }, 1000*i+1)
           ^^^^^^ --------------
      }
    

    But the problem now remains that your loop executes only once, so each callback is scheduled only once.