Search code examples
angularangular7

ViewContainerRef with ResizeObserver doesn't work properly in angular


I am trying to add and remove components dynamically depending on the the screen size using ComponentFactoryResolver.

@ViewChild('sidebarContainer', { read: ViewContainerRef }) sidebarContainer: ViewContainerRef;

_handleBodyResize() {
let self = this;
if (typeof ResizeObserver == 'undefined') return;

const obs = new ResizeObserver(entries => {
  let entry = entries[0];

  if ((entry.contentRect.width) < MOBILE_WIDTH) {
    if (this.deviceType !== DeviceType.mobile) {
      this.removeSidebar();

      this.deviceType = DeviceType.mobile;
    }
  } else {
    if (this.deviceType !== DeviceType.desktop) {
      this.addSidebar();

      this.deviceType = DeviceType.desktop;
    }
  };
});

removeSidebar() {
  this.sidebarContainer.clear();
  this.sidebarRef.destroy();
}

addSidebar() {
this.sidebarContainer.clear();
//_cfr is componentFactoryResolver
const compFactory = this._cfr.resolveComponentFactory(this.array[0]);

let comp = this.sidebarContainer.createComponent(compFactory);

this.sidebarRef = comp;
}

and the HTML

<div class="sidebar-container">
<div #sidebarContainer>

</div>

This sidebarContainer viewContainerRef can't seem to hold on to the references and creates new instance of the SidebarComponent whenever the with of the body goes from < 768px to >= 768px and doesn't delete the previously created component saved in sidebarRef and event the sidebarContainer.clear() method doesn't work

Strangely if I use window.addEventListener('resize'... it works.

Is there some underlying ResizeObserver technicality that I am not aware of and is there a way to make it work with ResizeObserver

Update

I forgot to mention this, but the code inside the resize observer executes on time and calls both of the functions appropriately.

The else section always creates a new instance of the SidebarComponent and renders it into view but this.sidebarContainer.clear() and this.sidebarRef.destroy don't remove the previously created instances even though while debugging, I can see that sidebarContainer and sidebarRef are not undefined and are relevant instances of ViewContainerRef and ComponentRef respectively.


Solution

  • Angular performs change detection in response to various triggers. DOM events, ajax requests, and timers/observables will trigger Angular's change detection.

    The window's resize event is an example of a DOM event that will trigger change detection.

    As far as I know, Angular's change detection is not automatically triggered by ResizeObserver. So you'll need to explicitly tell Angular to detect changes using ChangeDetectorRef.detectChanges():

    constructor(private changeDetector: ChangeDetectorRef) {}
    
    ngAfterViewInit() {
      const obs = new ResizeObserver(entries => {
        // Perform updates in response to resize
    
        // Then tell Angular to detect changes
        this.changeDetector.detectChanges();
      });
      obs.observe(this.resizeDiv.nativeElement);
    }
    

    Here's a StackBlitz example.

    If you only care about changes to the viewport dimensions, there's no need to use ResizeObserver. The window resize event will work fine in all browsers.