4

I'm trying to create a Angular service that components can use to watch the window resize event. After lots of attempts I finally found this solution worked best https://stackoverflow.com/a/43833815.

However, it seemed to cause an error when running in SSR.

TypeError: this.eventManager.addGlobalEventListener is not a function

After lots of attempts this is where I'm at:

Service

import { Injectable, Inject, PLATFORM_ID } from '@angular/core';
import { EventManager } from '@angular/platform-browser';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class WindowService {
  private resizeSubject: Subject<Window>;

  constructor(
    @Inject(PLATFORM_ID) private eventManager: EventManager) { // Event is not defined without @Inject(PLATFORM_ID) 
      this.resizeSubject = new Subject();

      this.eventManager.addGlobalEventListener('window', 'resize', this.onResize.bind(this));
  }

  private onResize(event: UIEvent) {
    this.resizeSubject.next(<Window>event.target);
  }

  /**
   * Get an observerable for the window resize event
   * @returns   Return the window resize as an observable
   */
  get onResize$(): Observable<Window> {
    return this.resizeSubject.asObservable();
  }
}

Component

import { Component, ElementRef, Input, OnInit, Output, EventEmitter, OnDestroy } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { Subscription } from 'rxjs/Subscription';

import { WindowService } from '../../services/window.service';


@Component({
  selector: 'app-sidebar',
  templateUrl: './sidebar.component.html'
})

export class SidebarComponent implements OnInit, OnDestroy {
  element: HTMLElement;
  resizeSubscription: Subscription;

  constructor(
    private readonly el: ElementRef,
    private windowService: WindowService) {
      this.element = el.nativeElement;
  }

  ngOnInit(): void {
    const self = this;

    this.resizeSubscription = this.windowService.onResize$
      .debounceTime(100)
      .subscribe(function(windowObj) {
        if (windowObj.innerWidth >= 1024) {
          self.open();
        } else {
          self.close();
        }
      });
  }

  ngOnDestroy(): void {
    if (this.resizeSubscription) {
      this.resizeSubscription.unsubscribe();
    }
  }

  ...
}

It seems a very complex way to bind to the window resize event but I've not found a better way that works with Angular SRR. Is there a recommended approach for this? Essentially I need to check the window size on resize (and also on load) and open or close the sidebar accordingly.

lawlesscreation
  • 649
  • 1
  • 6
  • 15
  • Is there something preventing you to use the following syntax in your service `window.onresize = ...` ? – Ploppy Jul 04 '18 at 17:23
  • @Ploppy I'm not sure that will work as Angular SSR won't know what "window" is? – lawlesscreation Jul 04 '18 at 17:26
  • I did not read carefully and missed the SSR part, but then, I think you are doing alright, you even debounce the observable. I don't see anything bad but wait for some more people to confirm. – Ploppy Jul 04 '18 at 17:35
  • 1
    Why don't you check the platform is to only add the event handler if you are client side? – David Jul 04 '18 at 18:17
  • @David I tried wrapping the `this.eventManager.addGlobalEventListener` in an isPlatform check which then loads server-side fine but when it transfers state to client-side it errors. I assume because at that point the WindowService constructor has already initialised and angular doesn't re-initialise the globalEventListener. – lawlesscreation Jul 05 '18 at 07:28
  • What's the error when it's transferred client side? – David Jul 05 '18 at 09:45

2 Answers2

1
<div (window:resize)="onResize($event)"

Method:

onResize(event) {
  event.target.innerWidth;
}

or

@HostListener('window:resize', ['$event'])
onResize(event) {
  event.target.innerWidth;
}

Supported global targets are window, document, and body.

Rohit.007
  • 3,414
  • 2
  • 21
  • 33
1

You can use platformId for write different code server and browser:

 constructor( @Inject(PLATFORM_ID) private platformId: Object, private eventManager: EventManager) {
      this.resizeSubject = new ReplaySubject();
    if (isPlatformBrowser(this.platformId)) {
      this.eventManager.addGlobalEventListener('window', 'resize', this.onResize.bind(this));
    }
  }

For user global window, document, event write like: https://github.com/Angular-RU/angular-universal-starter/blob/6d1e980655a55b2e1651ac5d040aaff80bc8fe8c/server.ts#L13

gorniv
  • 180
  • 2
  • 8