362

I would like to perform some tasks based on the window re-size event (on load and dynamically).

Currently I have my DOM as follows:

<div id="Harbour">
    <div id="Port" (window:resize)="onResize($event)" >
        <router-outlet></router-outlet>
    </div>
</div>

The event correctly fires

export class AppComponent {
    onResize(event) {
        console.log(event);
    }
}

How do I retrieve the Width and Height from this event object?

Thanks.

Totati
  • 1,489
  • 12
  • 23
DanAbdn
  • 7,151
  • 7
  • 27
  • 38
  • 8
    Not really an Angular question. look at the [window](https://developer.mozilla.org/en-US/docs/Web/API/Window) object. You can get it's `innerHeight` and `innerWidth` properties.. – Sasxa Feb 20 '16 at 18:48
  • 1
    @Sasxa is correct, you just need to do `console.log(event.target.innerWidth )` – Pankaj Parkar Feb 20 '16 at 19:10
  • Thanks for the info Sasxa/Pankaj - I wasn't sure whether it was just a plain javascript thing or a Typescript thing or an Angular event thing. I'm climbing a very steep learning curve for myself here and appreciate your input. – DanAbdn Feb 20 '16 at 20:18

19 Answers19

749
<div (window:resize)="onResize($event)"
onResize(event) {
  event.target.innerWidth;
}

or using the HostListener decorator:

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

Supported global targets are window, document, and body.

Until https://github.com/angular/angular/issues/13248 is implemented in Angular it is better for performance to subscribe to DOM events imperatively and use RXJS to reduce the amount of events as shown in some of the other answers.

clemlatz
  • 7,543
  • 4
  • 37
  • 51
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 22
    Is there any documentation about the syntax you use: **window:resize** ? – Clement Apr 25 '16 at 13:22
  • @Clement haven't found docs. But it was repeatedly mentioned and is officially supported. Docs are not perfect yet. – Günter Zöchbauer Apr 25 '16 at 13:28
  • Okay, so this is a kind of syntaxic sugar build into angular2 ? – Clement Apr 25 '16 at 13:41
  • 4
    Exactly. You can use `document`, `window`, and `body` https://github.com/angular/angular/blob/3412aba46e387b234d72e9194e2308f1d71a62df/modules/angular2/src/platform/server/parse5_adapter.ts#L533 – Günter Zöchbauer Apr 25 '16 at 13:45
  • Seems you're doing something wrong ;-) I'd suggest you create a new question with the code that allows to reproduce the problem. – Günter Zöchbauer Jan 04 '17 at 08:27
  • 1
    inside the onResize(event) let screenHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); It's working – gnganapath May 03 '17 at 06:16
  • 6
    perfect answer.. i do believe @HostListener is the cleaner way :) but make sure to import the HostListener first using `import { Component, OnInit, HostListener } from '@angular/core';` – Gaurav Sharma May 27 '17 at 15:07
  • 6
    Quick tip: If want to trigger on first load too, implement ngAfterViewInit from @angular/core. https://angular.io/api/core/AfterViewInit – Zymotik Nov 21 '17 at 14:44
  • 1
    Using Angular 6, `@HostListener('window:resize', ['$event'])` works for me, but only with `window`. Replacing `window` by `document` or `body`, the event is not triggered at all. – tom Aug 04 '18 at 14:58
  • 1
    Angular only providers what the browser supports. If `document` of body don't emit that event, youcan't getit from Angular. Consult the browser docs which events are amitted by which object. – Günter Zöchbauer Aug 04 '18 at 17:18
  • This is NOT THE BEST SOLUTION, hostlistener fires on every pixel, and multiple times, plus, if this is implemented in multiple components, will cause a lack or delays to show or change the elements – jcdsr Nov 27 '18 at 13:31
  • 1
    in fact, this will slow the app! – jcdsr Nov 27 '18 at 15:08
  • 1
    You're still right :D I added a link to an issue where plans for improvement are discussed. – Günter Zöchbauer Nov 27 '18 at 15:12
  • 1
    what about using debounceTime? – jcdsr Dec 11 '18 at 16:56
  • Is a good idea but works better with the other answers with imperative event subscription. – Günter Zöchbauer Dec 11 '18 at 17:00
  • 9
    Sure, however, for those who want to know how works HostListener with debounceTime, follow the link below https://plnkr.co/edit/3J0dcDaLTJBxkzo8Akyg?p=preview – jcdsr Dec 12 '18 at 11:23
  • @jcdsr Nice one! – Günter Zöchbauer Dec 12 '18 at 11:25
  • I'm using `@HostListener('window:resize') @throttle(300)`, but do I need to unsubscribe to the window:resize event and if so, how? – dude Mar 23 '20 at 14:14
  • 1
    @dude no. As a rule of thumb, if you subscribe imperatively (calling `foo.subscribe(evt => ...)`, you should explicitly unsubscribe, if you declarativly subscribe, Angular will take care automatically. – Günter Zöchbauer Mar 23 '20 at 15:20
  • @GünterZöchbauer Thanks for your reply. Do you have any reference for this? – dude Mar 23 '20 at 18:43
  • Hi, consider my answer, https://stackoverflow.com/a/57131782/4797961 debounceTime won't reduce change detection cycles. – Totati Jul 20 '20 at 11:56
  • The only thing i couldn't figure out right away was that the `onResize` method has to be defined right after the @HostListener declaration in a kind of a callback way. Had not used this API before. – oomer Oct 29 '20 at 13:58
  • 1
    @oomer `@HostListene()` is an annotation to `onResize` similar to `@Input`, `@Output()`, `@ViewChild()` `@Inject()`, ... and annotations always apply to the following element. – Günter Zöchbauer Oct 29 '20 at 14:24
  • 1
    The global targets has been moved, currently you can find them here: https://github.com/angular/angular/blob/master/packages/compiler/src/render3/view/template.ts#L56 – Totati Oct 04 '21 at 09:06
124

I know this was asked a long time ago, but there is a better way to do this now! I'm not sure if anyone will see this answer though. Obviously your imports:

import { fromEvent, Observable, Subscription } from "rxjs";

Then in your component:

resizeObservable$: Observable<Event>
resizeSubscription$: Subscription

ngOnInit() {
    this.resizeObservable$ = fromEvent(window, 'resize')
    this.resizeSubscription$ = this.resizeObservable$.subscribe( evt => {
      console.log('event: ', evt)
    })
}

Then be sure to unsubscribe on destroy!

ngOnDestroy() {
    this.resizeSubscription$.unsubscribe()
}
Yannick
  • 13
  • 4
Chris Stanley
  • 2,766
  • 2
  • 13
  • 18
  • 1
    The only way that has worked for me. Thanks!! :-) ... I just had to adjust your import. Perhaps my rxjs is newer: `import { fromEvent, Observable,Subscription } from "rxjs";` – Jette Dec 11 '18 at 14:10
  • 1
    Where can I add debounce(1000) in this? – Deepak Thomas Jan 21 '19 at 07:06
  • 7
    Sorry for late reply: to add debounce, you'll want to use `this.resizeSubscription$ = this.resizeObservable$.pipe(debounceTime(1000)).subscribe( evt => { console.log('event: ', evt) })` – Chris Stanley Feb 05 '19 at 17:33
  • 2
    for debounce, import { debounceTime } from 'rxjs/operators'; – CRice Nov 05 '20 at 02:33
  • In spite of using `debounceTime` and running your subscribe callback less, Angular will run change detection on every event, as it's triggered inside the zone. You should check my answer that eliminates this problem. – Totati Oct 04 '21 at 09:19
  • Andddd if you don't want it to accumulate subscriptions each time the window resizes, do a this.resizeSubscription?.unsubscribe() just before you subscribe in ngOnInit! – Collin Aug 16 '22 at 15:51
80

@Günter's answer is correct. I just wanted to propose yet another method.

You could also add the host-binding inside the @Component()-decorator. You can put the event and desired function call in the host-metadata-property like so:

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  host: {
    '(window:resize)': 'onResize($event)'
  }
})
export class AppComponent{
   onResize(event){
     event.target.innerWidth; // window width
   }
}
John
  • 10,165
  • 5
  • 55
  • 71
  • 3
    I've tried every method on this page and it seems none of them work. Is there any official documentation about this. – Tomato Jan 02 '17 at 10:47
  • how is the viewport height obtained on load? – Giridhar Karnik Jan 04 '17 at 18:35
  • @GiridharKarnik, have you tried doing a `window.innerWidth` inside `ngOnInit`, or `ngAfterViewInit` methods? – John Feb 27 '17 at 07:32
  • @Tomato The API-reference can be found [here](https://angular.io/docs/ts/latest/api/) Many of the references are autogenerated (I presume), and have a lack of examples or detailed explanation. Some api-references have a lot of examples. I could not find a concrete example from the docs about this though. Maybe it is hidden somewhere :P – John Feb 27 '17 at 08:03
  • @John, if I do that, won't it be an on shot thing? Will ngOnInit or ngAfterViewInit fire each time I resize? – Giridhar Karnik Feb 27 '17 at 08:05
  • @GiridharKarnik Yes, you are correct. They will only fire once (each time the component is created). If you want to check every time you resize, use the `@HostListener`. – John Feb 27 '17 at 08:08
  • 1
    [here](https://github.com/angular/angular/blob/e894f5c39951573e695b74456cc4d980bc49db02/aio/src/app/app.component.ts#L215) is a reference of how its used – Anand Rockzz Jun 14 '17 at 06:05
  • doesn't work for server-side or in angular universal – jcdsr Apr 23 '20 at 11:27
49

There's a ViewportRuler service in angular CDK. It runs outside of the zone, supports orientationchange and resize. It works with server side rendering too.

@Component({
  selector: 'my-app',
  template: `
    <p>Viewport size: {{ width }} x {{ height }}</p>
  `
})
export class AppComponent implements OnDestroy {
  width: number;
  height: number;
  private readonly viewportChange = this.viewportRuler
    .change(200)
    .subscribe(() => this.ngZone.run(() => this.setSize()));
  constructor(
    private readonly viewportRuler: ViewportRuler,
    private readonly ngZone: NgZone
  ) {
    // Change happens well, on change. The first load is not a change, so we init the values here. (You can use `startWith` operator too.)
    this.setSize();
  }
  // Never forget to unsubscribe!
  ngOnDestroy() {
    this.viewportChange.unsubscribe();
  }

  private setSize() {
    const { width, height } = this.viewportRuler.getViewportSize();
    this.width = width;
    this.height = height;
  }
}

Stackblitz example for ViewportRuler

The benefit is, that it limits change detection cycles (it will trigger only when you run the callback in the zone), while (window:resize) will trigger change detection every time it gets called.

Totati
  • 1,489
  • 12
  • 23
44

The correct way to do this is to utilize the EventManager class to bind the event. This allows your code to work in alternative platforms, for example server side rendering with Angular Universal.

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

@Injectable()
export class ResizeService {

  get onResize$(): Observable<Window> {
    return this.resizeSubject.asObservable();
  }

  private resizeSubject: Subject<Window>;

  constructor(private eventManager: EventManager) {
    this.resizeSubject = new Subject();
    this.eventManager.addGlobalEventListener('window', 'resize', this.onResize.bind(this));
  }

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

Usage in a component is as simple as adding this service as a provider to your app.module and then importing it in the constructor of a component.

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'my-component',
  template: ``,
  styles: [``]
})
export class MyComponent implements OnInit {

  private resizeSubscription: Subscription;

  constructor(private resizeService: ResizeService) { }

  ngOnInit() {
    this.resizeSubscription = this.resizeService.onResize$
      .subscribe(size => console.log(size));
  }

  ngOnDestroy() {
    if (this.resizeSubscription) {
      this.resizeSubscription.unsubscribe();
    }
  }
}
cgatian
  • 22,047
  • 9
  • 56
  • 76
  • This doesn't ever fire on mobile as far as I can tell. How do you get the initial window size with this approach? – user3170530 Jul 28 '17 at 06:10
  • Mobile does not have a `window` object, just as a server doesn't have a `window`. Im not familiar with the different mobile configurations but you should be able to easily adapt the above code to bind to the correct global event listener – cgatian Jul 28 '17 at 14:18
  • @cgatian I'm a noob but this seems like the right answer. Unfortunatly I'm having no luck making my subscription Log in the component. Could you add to the answer how to subscribe to this in a component so that one can see the updates? – Armeen Moon Sep 25 '17 at 04:08
  • @cgatian I'll make a plunker but this seemed to not work . The filter in the service seems weird tho https://stackoverflow.com/q/46397531/1191635 – Armeen Moon Sep 26 '17 at 13:55
  • 5
    @cgatian `Mobile does not have a window object`.... why do you think mobile browsers don't have a window object? – Drenai Jan 09 '18 at 12:13
  • I took the comment as he was using Ionic or NativeScript. – cgatian Jan 10 '18 at 01:11
  • Sorry I acknowledge that comment may be incorrect and misleading. I don't know about mobile – cgatian Aug 13 '20 at 12:17
  • 1
    Note: `addGlobalEventListener` is @deprecated as of Angular 12 — No longer being used in Ivy code. To be removed in version 14. – GusSL Sep 20 '21 at 03:53
34

Here is a better way to do it. Based on Birowsky's answer.

Step 1: Create an angular service with RxJS Observables.

import { Injectable } from '@angular/core';
import { Observable, BehaviorSubject } from 'rxjs';

@Injectable()
export class WindowService {
    height$: Observable<number>;
    //create more Observables as and when needed for various properties
    hello: string = "Hello";
    constructor() {
        let windowSize$ = new BehaviorSubject(getWindowSize());

        this.height$ = (windowSize$.pluck('height') as Observable<number>).distinctUntilChanged();

        Observable.fromEvent(window, 'resize')
            .map(getWindowSize)
            .subscribe(windowSize$);
    }

}

function getWindowSize() {
    return {
        height: window.innerHeight
        //you can sense other parameters here
    };
};

Step 2: Inject the above service and subscribe to any of the Observables created within the service wherever you would like to receive the window resize event.

import { Component } from '@angular/core';
//import service
import { WindowService } from '../Services/window.service';

@Component({
    selector: 'pm-app',
    templateUrl: './componentTemplates/app.component.html',
    providers: [WindowService]
})
export class AppComponent { 

    constructor(private windowService: WindowService) {

        //subscribe to the window resize event
        windowService.height$.subscribe((value:any) => {
            //Do whatever you want with the value.
            //You can also subscribe to other observables of the service
        });
    }

}

A sound understanding of Reactive Programming will always help in overcoming difficult problems. Hope this helps someone.

Community
  • 1
  • 1
Giridhar Karnik
  • 2,213
  • 4
  • 27
  • 47
  • I believe this is a error: this.height$ = (windowSize$.pluck('height') as Observable).distinctUntilChanged();Observable).distinctUntilChanged(); looks like you pasted in the distinctUntilChanged() twice in a row – Zuriel Jan 24 '17 at 14:44
  • I did not get you, eloborate please. – Giridhar Karnik May 22 '17 at 14:00
  • Change detection won't be fired as you're doing this event outside of Angular, believe that's what he meant. – Mark Pieszak - Trilon.io Jun 20 '17 at 14:36
  • 1
    I had an error saying 'pluck' does not exist on type BehaviorSubject. Changing the code to this.height$ = windowSize$.map(x => x.height) worked for me. – Matt Sugden Jul 07 '17 at 11:51
  • @GiridharKamik Can you provide a solution that would subscribe on width & height at same time, which we could subscribe to both the width, height in 1 simple subscribe – ghiscoding Sep 13 '17 at 20:14
  • Would this work with Server Side Rendering or with WebWorkers... `height: window.innerHeight` ? – Drenai Jan 09 '18 at 12:05
  • @MarkPieszak-Trilon.io It will fire, cause it runs inside the zone. I don't see calling the subscribe inside `runOutsideAngular` – Totati Apr 21 '20 at 15:57
  • 1
    in the service constructor, you have to use `.pipe()` to use `distinctUntilChanged()`, `pluck()` and `map()`, both imported from `rxjs/operators` used like this: `.pipe(map())`. Also, just use `fromEvent()` imported directly from `rxjs` instead of `Observable.fromEvent()` – GusSL Sep 20 '21 at 04:07
13

I haven't seen anyone talking about MediaMatcher of angular/cdk.

You can define a MediaQuery and attach a listener to it - then anywhere on your template (or ts) you can invoke stuff if the Matcher is matched. LiveExample

App.Component.ts

import {Component, ChangeDetectorRef} from '@angular/core';
import {MediaMatcher} from '@angular/cdk/layout';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  mobileQuery: MediaQueryList;

  constructor(changeDetectorRef: ChangeDetectorRef, media: MediaMatcher) {
    this.mobileQuery = media.matchMedia('(max-width: 600px)');
    this._mobileQueryListener = () => changeDetectorRef.detectChanges();
    this.mobileQuery.addListener(this._mobileQueryListener);
  }

  private _mobileQueryListener: () => void;

  ngOnDestroy() {
    this.mobileQuery.removeListener(this._mobileQueryListener);
  }

}

App.Component.Html

<div [class]="mobileQuery.matches ? 'text-red' : 'text-blue'"> I turn red on mobile mode 
</div>

App.Component.css

.text-red { 
   color: red;
}

.text-blue {
   color: blue;
}

source: https://material.angular.io/components/sidenav/overview

Stavm
  • 7,833
  • 5
  • 44
  • 68
6

Assuming that < 600px means mobile to you, you can use this observable and subscribe to it:

First we need the current window size. So we create an observable which only emits a single value: the current window size.

initial$ = Observable.of(window.innerWidth > 599 ? false : true);

Then we need to create another observable, so that we know when the window size was changed. For this we can use the "fromEvent" operator. To learn more about rxjs`s operators please visit: rxjs

resize$ = Observable.fromEvent(window, 'resize').map((event: any) => {
  return event.target.innerWidth > 599 ? false : true;
 });

Merg these two streams to receive our observable:

mobile$ = Observable.merge(this.resize$, this.initial$).distinctUntilChanged();

Now you can subscribe to it like this:

mobile$.subscribe((event) => { console.log(event); });

Remember to unsubscribe :)

Flosut Mözil
  • 395
  • 4
  • 12
5

If you want just one event after the resize is finished, it's better to use RxJS with debounceTime : debounceTime: Discard emitted values that take less than the specified time between output.

He waits > 0.5s between 2 events emitted before running the code. In simpler terms, it waits for the resizing to be finished before executing the next code.

// RxJS v6+
import { fromEvent } from 'rxjs';
import { debounceTime, map } from 'rxjs/operators';

...

const resize$ = fromEvent(window, 'resize');
    resize$
      .pipe(
        map((i: any) => i),
        debounceTime(500) // He waits > 0.5s between 2 events emitted before running the next.
      )
      .subscribe((event) => {
        console.log('resize is finished');
      });

StackBlitz example

StackBlitz

A. Morel
  • 9,210
  • 4
  • 56
  • 45
4

This is not exactly answer for the question but it can help somebody who needs to detect size changes on any element.

I have created a library that adds resized event to any element - Angular Resize Event.

It internally uses ResizeSensor from CSS Element Queries.

Example usage

HTML

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

TypeScript

@Component({...})
class MyComponent {
  width: number;
  height: number;

  onResized(event: ResizedEvent): void {
    this.width = event.newWidth;
    this.height = event.newHeight;
  }
}
Martin Volek
  • 1,079
  • 3
  • 12
  • 27
4

I checked most of these answers. then decided to check out Angular documentation on Layout.

Angular has its own Observer for detecting different sizes and it is easy to implement into the component or a Service.

a simpl example would be:

import {BreakpointObserver, Breakpoints} from '@angular/cdk/layout';

@Component({...})
class MyComponent {
  constructor(breakpointObserver: BreakpointObserver) {
    breakpointObserver.observe([
      Breakpoints.HandsetLandscape,
      Breakpoints.HandsetPortrait
    ]).subscribe(result => {
      if (result.matches) {
        this.activateHandsetLayout();
      }
    });
  }
}

hope it helps

Mohamed Aljamil
  • 389
  • 1
  • 9
  • 2
    This is nice to know... but just a note... this is material library angular docs, not official documentation from Angular itself. Sort of suggestive that all the breakpoints will follow Material guidelines OOTB. – Sean Halls Sep 22 '20 at 00:05
3

Based on the solution of @cgatian I would suggest the following simplification:

import { EventManager } from '@angular/platform-browser';
import { Injectable, EventEmitter } from '@angular/core';

@Injectable()
export class ResizeService {

  public onResize$ = new EventEmitter<{ width: number; height: number; }>();

  constructor(eventManager: EventManager) {
    eventManager.addGlobalEventListener('window', 'resize',
      e => this.onResize$.emit({
        width: e.target.innerWidth,
        height: e.target.innerHeight
      }));
  }
}

Usage:

import { Component } from '@angular/core';
import { ResizeService } from './resize-service';

@Component({
  selector: 'my-component',
  template: `{{ rs.onResize$ | async | json }}`
})
export class MyComponent {
  constructor(private rs: ResizeService) { }
}
Johannes Hoppe
  • 251
  • 3
  • 10
  • I found this as the best solution, however, this only works when the window is resized, but not when loads or when the router change. Do you know, how to apply with router change, reload or load? – jcdsr Nov 22 '18 at 09:29
  • You can add a function to the service and trigger it in component @jcdsr: getScreenSize() { this.onResize$.emit({ width: window.innerWidth, height: window.innerHeight }); } – DanielWaw Jan 28 '20 at 12:22
2

On Angular2 (2.1.0) I use ngZone to capture the screen change event.

Take a look on the example:

import { Component, NgZone } from '@angular/core';//import ngZone library
...
//capture screen changed inside constructor
constructor(private ngZone: NgZone) {
    window.onresize = (e) =>
    {
        ngZone.run(() => {
            console.log(window.innerWidth);
            console.log(window.innerHeight);
        });
    };
}

I hope this help!

Anh Hoang
  • 2,242
  • 3
  • 22
  • 23
2

I wrote this lib to find once component boundary size change (resize) in Angular, may this help other people. You may put it on the root component, will do the same thing as window resize.

Step 1: Import the module

import { BoundSensorModule } from 'angular-bound-sensor';

@NgModule({
  (...)
  imports: [
    BoundSensorModule,
  ],
})
export class AppModule { }

Step 2: Add the directive like below

<simple-component boundSensor></simple-component>

Step 3: Receive the boundary size details

import { HostListener } from '@angular/core';

@Component({
  selector: 'simple-component'
  (...)
})
class SimpleComponent {
  @HostListener('resize', ['$event'])
  onResize(event) {
    console.log(event.detail);
  }
}
PRAISER
  • 793
  • 7
  • 15
2

Below code lets observe any size change for any given div in Angular.

<div #observed-div>
</div>

then in the Component:

oldWidth = 0;
oldHeight = 0;

@ViewChild('observed-div') myDiv: ElementRef;
ngAfterViewChecked() {
  const newWidth = this.myDiv.nativeElement.offsetWidth;
  const newHeight = this.myDiv.nativeElement.offsetHeight;
  if (this.oldWidth !== newWidth || this.oldHeight !== newHeight)
    console.log('resized!');

  this.oldWidth = newWidth;
  this.oldHeight = newHeight;
}
abedfar
  • 1,989
  • 24
  • 21
1

Here is an update to @GiridharKamik answer above with the latest version of Rxjs.

import { Injectable } from '@angular/core';
import { Observable, BehaviorSubject, fromEvent } from 'rxjs';
import { pluck, distinctUntilChanged, map } from 'rxjs/operators';

@Injectable()
export class WindowService {
    height$: Observable<number>;
    constructor() {
        const windowSize$ = new BehaviorSubject(getWindowSize());

        this.height$ = windowSize$.pipe(pluck('height'), distinctUntilChanged());

        fromEvent(window, 'resize').pipe(map(getWindowSize))
            .subscribe(windowSize$);
    }

}

function getWindowSize() {
    return {
        height: window.innerHeight
        //you can sense other parameters here
    };
};
Post Impatica
  • 14,999
  • 9
  • 67
  • 78
1

Here is a simple and clean solution I created so I could inject it into multiple components.

ResizeService.ts

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class ResizeService {

  constructor() {

    window.addEventListener('resize', (e) => {
      this.onResize.next();
    });

  }

  public onResize = new Subject();

}

In use:

constructor(
  private resizeService: ResizeService
) { 

  this.subscriptions.push(this.resizeService.onResize.subscribe(() => {
    // Do stuff
  }));

}

private subscriptions: Subscription[] = [];
Reed
  • 1,515
  • 1
  • 21
  • 38
1

What I did is as follows, much like what Johannes Hoppe suggested:

import { EventManager } from '@angular/platform-browser';
import { Injectable, EventEmitter } from '@angular/core';

@Injectable()
export class ResizeService {

  public onResize$ = new EventEmitter<{ width: number; height: number; }>();

  constructor(eventManager: EventManager) {
    eventManager.addGlobalEventListener('window', 'resize',
      event => this.onResize$.emit({
        width: event.target.innerWidth,
        height: event.target.innerHeight
      }));
  }
  
  getWindowSize(){
    this.onResize$.emit({
    width: window.innerWidth,
    height: window.innerHeight
    });
  }
}

In app.component.ts:

Import { ResizeService } from ".shared/services/resize.service"
import { Component } from "@angular/core"

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent{
  windowSize: {width: number, height: number};
  
  constructor(private resizeService: ResizeService){
  }

  ngOnInit(){
    this.resizeService.onResize$.subscribe((value) => {
      this.windowSize = value;
    });
    
    this.resizeService.getWindowSize();
  }
}

Then in your app.component.html:

<router-outlet *ngIf = "windowSize?.width > 1280 && windowSize?.height > 700; else errorComponent">
</router-outlet>

<ng-template #errorComponent>
  <app-error-component></app-error-component>
</ng-template>
0

Another approach that I took was

import {Component, OnInit} from '@angular/core';
import {fromEvent} from "rxjs";
import {debounceTime, map, startWith} from "rxjs/operators";


function windowSizeObserver(dTime = 300) {
  return fromEvent(window, 'resize').pipe(
    debounceTime(dTime),
    map(event => {
      const window = event.target as Window;

      return {width: window.innerWidth, height: window.innerHeight}
    }),
    startWith({width: window.innerWidth, height: window.innerHeight})
  );
}

@Component({
  selector: 'app-root',
  template: `
    <h2>Window Size</h2>
    <div>
      <span>Height: {{(windowSize$ | async)?.height}}</span>
      <span>Width: {{(windowSize$ | async)?.width}}</span>
    </div>
  `
})
export class WindowSizeTestComponent {
  windowSize$ = windowSizeObserver();
}

here the windowSizeObserver can be reused in any component

gsaandy
  • 581
  • 4
  • 8