How to perform change detection on div resizing
Asked Answered
T

4

25

I use Bootstrap for my layout and I need to check if the automatic calculated size of my div with bootstrap for example width = 25% is changed.

How to perform change detection on a attribute which I don't set in my template, but is set by Bootstrap?

(window:resize) is not enough.

Thebault answered 18/4, 2016 at 13:47 Comment(0)
B
36

You can add a directive to the div and implement lifecycle hook ngDoCheck() to perform your own change detection logic. You'll need to inject ElementRef:

constructor(private _elRef:ElementRef) {}
ngDoCheck() {
   // use ElementRef to examine the div property of interest

If the div is inside a component template, add a local template variable

<div #myDiv ...>

Then use @ViewChild() to get a reference to the div and implement lifecycle hook ngDoCheck() or ngAfterViewChecked() to perform your own change detection logic.

@ViewChild('myDiv') theDiv:ElementRef;
ngAfterViewChecked() {
   // use this.theDiv to examine the div property of interest

Note that ngDoCheck() and ngAfterViewChecked() will be called every time change detection runs. See also the dev guide Lifecycle Hooks.

Borecole answered 18/4, 2016 at 15:10 Comment(4)
The solution in this problem does not work for me because I need to know the real SIZE (w x h) of the div. My div has a relative width 33,33% and this does not change. But the effective size of the DIV changed on do some things in my APP. the problem is if I do in ngDoCheck something like that new Ruler(DOM).measure(this.elementRef).then((rect: Rectangle) => { console.log(rect.width); }); } to check the real width I get a loop, I don't know why but calling masure whil do a change on dom and so there is a infite loop on componente. Some ideas? But I can use this solution for other problems =)Thebault
measure() (or the promise it returns) is likely monkey-patched by Zone.js, so when the asynchronous event fires (and after the event handler runs), Angular's hook runs change detection again. You can solve this problem by running your code outside of the Angular zone. Inject NgZone, then check your div size outside the zone: this._ngZone.runOutsideAngular(_ => { // ... check div size here });.Borecole
I implement your solution, doing a relative complicate logic to check if I had to reset the height, because DoCheck is called also on much other events, for example on hovering the div but there is no need to reset the height. DoCheck is not called on resize window, so I also added (window:resize) to component which calls the same logic to set and calc new height if needed.Thebault
Avoid using doCheck as much as you can; if not configured correctly, could cause huge performance issues.Antidromic
K
12

I had the same problem so I used the lightweight library called Angular-Resize-Event

https://www.npmjs.com/package/angular-resize-event

<div (resized)="onResized($event)"></div>

After installing it just add this output event to any div which you want to check for size changes.

Komarek answered 25/2, 2019 at 10:59 Comment(1)
a warm reminder, child component might need to call changeDetectionReference if there is a change in its values to notify parent and avoid expressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked, e.g. this.cdr.detectChanges();Vernettaverneuil
T
-1

I have tried the correct answer but the refresh rate wasn't very accurate, So I looked for another solution.

Here it is

// Directive
@Directive({
  selector: '[resize-detector]',
})

public constructor(public element: ElementRef<HTMLElement>) {}

public ngOnInit(): void {
// Call detectResize as whe the element inits the windows resize event most likely won't trigger
  this.detectResize();
}

// if you need the windowDimensions add ", [$event]" to @HostListener and pass event to detectResize(event)
@HostListener('window:resize') 
public detectResize(): void {
  console.log(element.nativeElement.offsetWidth);
  console.log(element.nativeElement.offsetHeight);
  // Do you magic here ...
}

Then you can use it in any element like:

<app-component resize-detector></app-component>
Titograd answered 23/11, 2018 at 0:19 Comment(1)
It works only when resizing the window, but if there is some interactive element changing our target's size, it won't be recorded.Charron
S
-3

There are few methods to track DOM attribute changes:

  • polling i.e. setInterval
  • DOM mutation events: DOMAttrModified + propertychange for IE
  • implementing MutationObserver
Soporific answered 18/4, 2016 at 15:28 Comment(8)
MutuationObserver works good, but I noticed I need a little bit more than an observer for attribute, because my DIV WIDTH is always 33,33% I need to know when the real calculated SIZE of my DIC container changed. Some idea?Thebault
you need something like window.getComputedStyle(element)Soporific
yea, but when should I call this? I can't call it on MutationObserver because the attribute is not changed it is still 33,33%Thebault
ok, there is another approachSoporific
ok, but there are still several problems 1) I'm not able to attach an listener for 'ontransitionend' it seems there is no event for this in angular2 2) as you can see on this plnkr plnkr.co/edit/UJ9gOpBJti30oTx5lhLL?p=preview there are a containerDIV and a myDIV, on click button for resize containerDIV the transition does not work for myDIV, it works only if I set the width of myDIV ... but my problem is the wrapped component using bootstrap and width 33,33% is autside my component, my component self has 100% width so, the div is resized becouse it is 100% but there is no transitionThebault
the only solution that came to mind is using css media queries. check this librarySoporific
that could be also a good solution, for now I resolved my problem yesterday with the first answer by @Mark Rajcok, I implement a DoCheck livecycle and do there a relative complicate logic to check if I realy will calculate my size new getting the width value by getComputedStyle, because DoCheck is called to much often also on hover the div, and there I want to recalc the height of the div. I have also set a (window:resize) to component because DoCheck is not called on window resize, I don't know whyThebault
@MichaelBurger, Angular doesn't monkey-patch the window resize event, hence the reason ngDoCheck() is not called when you resize a window.Borecole

© 2022 - 2024 — McMap. All rights reserved.