I have created an Angular 2 app which contains a ponent whose HTML element I am trying to position relative to that of another ponent. In order to pute 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 ponent.
However, it seems ngAfterViewChecked
is not called when the rendering leads to new puted style properties, as I found that my hook is not called any more, even though the puted 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 puted 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 puted 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 ..]
}
I have created an Angular 2 app which contains a ponent whose HTML element I am trying to position relative to that of another ponent. In order to pute 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 ponent.
However, it seems ngAfterViewChecked
is not called when the rendering leads to new puted style properties, as I found that my hook is not called any more, even though the puted 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 puted 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 puted 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 ..]
}
Share
Improve this question
asked Apr 27, 2017 at 13:16
not2savvynot2savvy
4,3614 gold badges33 silver badges57 bronze badges
3 Answers
Reset to default 2See the top diagram here to view the lifecycle diagram. If your hook in doCheck doesn't work, the AfterViewChecked won't work either since doCheck initiates AfterViewChecked.
Every time an async process pletes, another round of doCheck occurs. That would call AfterViewChecked, and that would call your updateView function. SetTimout() is an async process. I suspect the code above would loop infinitely as is.
I believe what you want is to have the parent ponent of what you have shown here use ngAfterViewChecked() and have that call updateView(). That topic is explored here.
The Angular way to make two ponents work together is to have a parent ponent that can municate changes between the child ponents, since the parent ponent is the context that can see events occurring in the children.
The solution I would personally use would be an EventEmitter, in the ponent not shown, in the ngOnInit() function. It would inform the parent that it is initialized. Then, in the parent ponent, have a function call that occurs on the emitted event that tells the child ponent you have here to updateView.
In vanilla JavaScript you could use:
MutationObserver
and trigger you own function when a DOM change is made.
MutationObserver provides developers with a way to react to changes in a DOM. It is designed as a replacement for Mutation Events defined in the DOM3 Events specification.
Why isn't the ngAfterViewChecked called when the rendering leads to new puted style properties?
The Angular framework does not detect changes in the puted 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 bee 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 puted style changes?
There seems to be no other way than using setTimeout
in order to watch changes in the puted style of DOM elements.