javascriptcssangulardomgetcomputedstyle

How to properly detect a change in computed style property with Angular 2?


I have created an Angular 2 app which contains a component whose HTML element I am trying to position relative to that of another component. In order to compute the exact coordinates, I need to know my own element's width and height as rendered by the browser.

I am using window.getComputedStyle(this.elementRef.nativeElement) to get those properties. I noticed that, as the page is being rendered, the properties keep changing until the page rendering has finished. In order to get informed about any changes and adjust the positioning, I check the values in a ngAfterViewChecked method of my component.

However, it seems ngAfterViewChecked is not called when the rendering leads to new computed style properties, as I found that my hook is not called any more, even though the computed style properties are still changing. I assume that the Angular framework is not designed to detect such a change.

My first attempt was to implement the ngDoCheck hook, but it seems this one isn't called either after some time and before the computed style property has its final value. I assume that I do not yet fully understand when exactly this hook is intended to be called.

I finally found out that, if I implement a setTimeout function in ngAfterViewChecked, this leads to this same hook being called again later, even if I pass only a dummy function like:

setTimeout(() => {}, 500);

But frankly, I do not understand why this so. Can someone please explain to me the connection between the setTimeout and the ngAfterViewChecked hook?

And while this occurs to be a somewhat dirty work-around: What is the proper 'Angular' way to detect and handle a change in computed style attributes?


Code excerpt:

export class MyComponent implements OnInit, AfterViewChecked
{
  private cssWidth: number;
  private cssHeight: number;

  constructor(private elementRef: ElementRef, private renderer: Renderer2)
  {
  }

  ngAfterViewChecked()
  {
    console.log("ngAfterViewChecked");
    this.updateView();
  }

  public updateView()
  {
    const sizeX = parseFloat(window.getComputedStyle(this.elementRef.nativeElement).width) || 0;
    const sizeY = parseFloat(window.getComputedStyle(this.elementRef.nativeElement.height) || 0;

    if (sizeX === this.cssWidth && sizeY === this.cssHeight) {
      // no change
      return;
    }

    console.log("Change detected!");

    // TODO Does not work without this dummy timeout function (else no more changes detected) - why so??
    setTimeout(() => {}, 500);

    [.. doing the positioning here ..]
 }

Solution

  • Why isn't the ngAfterViewChecked called when the rendering leads to new computed style properties?

    The Angular framework does not detect changes in the computed style of DOM elements, at least not when they are caused by the rendering engine of the browser. Consequently, the ngAfterViewChecked hook will not be called in such cases.

    Why does it work when using setTimeout?

    As explained in Michael Palmer's answer, the setTimeout causes Angular to become aware of an asynchronous process that might cause a change. Therefore, the ngAfterViewChecked hook is called after the timeout function has been performed. Angular does not try to check what exactly is done in the setTimeout function and whether or not this is something that actually affects the view. Obviously, this would be quite hard to implement and probably not worth the effort. Instead, Angular calls the ngAfterViewChecked hook just in case something has happened in the asynchronous process that affects the view.

    What is the Angular way to listen to computed style changes?

    There seems to be no other way than using setTimeout in order to watch changes in the computed style of DOM elements.