Angular window resize event

385,756

Solution 1

<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.

Solution 2

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()
}

Solution 3

@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
   }
}

Solution 4

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();
    }
  }
}

Solution 5

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.

Share:
385,756

Related videos on Youtube

DanAbdn
Author by

DanAbdn

Programmer based at Aberdeen, UK. Primarily interests relate around web technologies: MVC 6 with C# Single Page Applications (SPA) - Angular2 SignalR WebGL / OpenGL Visual Studio 2015

Updated on July 29, 2022

Comments

  • DanAbdn
    DanAbdn 10 months

    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.

    • Sasxa over 7 years
      Not really an Angular question. look at the window object. You can get it's innerHeight and innerWidth properties..
    • Pankaj Parkar
      Pankaj Parkar over 7 years
      @Sasxa is correct, you just need to do console.log(event.target.innerWidth )
    • DanAbdn
      DanAbdn over 7 years
      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.
  • Clement
    Clement about 7 years
    Is there any documentation about the syntax you use: window:resize ?
  • Günter Zöchbauer
    Günter Zöchbauer about 7 years
    @Clement haven't found docs. But it was repeatedly mentioned and is officially supported. Docs are not perfect yet.
  • Clement
    Clement about 7 years
    Okay, so this is a kind of syntaxic sugar build into angular2 ?
  • Günter Zöchbauer
    Günter Zöchbauer about 7 years
    Exactly. You can use document, window, and body github.com/angular/angular/blob/…
  • Tomato over 6 years
    I've tried every method on this page and it seems none of them work. Is there any official documentation about this.
  • Günter Zöchbauer
    Günter Zöchbauer over 6 years
    Seems you're doing something wrong ;-) I'd suggest you create a new question with the code that allows to reproduce the problem.
  • Giridhar Karnik
    Giridhar Karnik over 6 years
    how is the viewport height obtained on load?
  • Zuriel over 6 years
    I believe this is a error: this.height$ = (windowSize$.pluck('height') as Observable<number>).distinctUntilChanged();Observable<number‌​>).distinctUntilChan‌​ged(); looks like you pasted in the distinctUntilChanged() twice in a row
  • John
    John about 6 years
    @GiridharKarnik, have you tried doing a window.innerWidth inside ngOnInit, or ngAfterViewInit methods?
  • John
    John about 6 years
    @Tomato The API-reference can be found here 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
  • Giridhar Karnik
    Giridhar Karnik about 6 years
    @John, if I do that, won't it be an on shot thing? Will ngOnInit or ngAfterViewInit fire each time I resize?
  • John
    John about 6 years
    @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.
  • gnganapath
    gnganapath about 6 years
    inside the onResize(event) let screenHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); It's working
  • Giridhar Karnik
    Giridhar Karnik about 6 years
    I did not get you, eloborate please.
  • Gaurav Sharma
    Gaurav Sharma almost 6 years
    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';
  • Anand Rockzz
    Anand Rockzz almost 6 years
    here is a reference of how its used
  • Mark Pieszak - Trilon.io almost 6 years
    Change detection won't be fired as you're doing this event outside of Angular, believe that's what he meant.
  • Matt Sugden almost 6 years
    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.
  • user3170530
    user3170530 almost 6 years
    This doesn't ever fire on mobile as far as I can tell. How do you get the initial window size with this approach?
  • cgatian
    cgatian almost 6 years
    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
  • ghiscoding
    ghiscoding over 5 years
    @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
  • Armeen Moon
    Armeen Moon over 5 years
    @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
    Armeen Moon over 5 years
    @cgatian I'll make a plunker but this seemed to not work . The filter in the service seems weird tho stackoverflow.com/q/46397531/1191635
  • Zymotik
    Zymotik over 5 years
    Quick tip: If want to trigger on first load too, implement ngAfterViewInit from @angular/core. angular.io/api/core/AfterViewInit
  • Drenai
    Drenai over 5 years
    Would this work with Server Side Rendering or with WebWorkers... height: window.innerHeight ?
  • Drenai
    Drenai over 5 years
    @cgatian Mobile does not have a window object.... why do you think mobile browsers don't have a window object?
  • cgatian
    cgatian over 5 years
    I took the comment as he was using Ionic or NativeScript.
  • tom
    tom almost 5 years
    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.
  • Günter Zöchbauer
    Günter Zöchbauer almost 5 years
    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.
  • jcdsr
    jcdsr over 4 years
    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
    jcdsr over 4 years
    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
    jcdsr over 4 years
    in fact, this will slow the app!
  • Günter Zöchbauer
    Günter Zöchbauer over 4 years
    You're still right :D I added a link to an issue where plans for improvement are discussed.
  • Jette
    Jette over 4 years
    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";
  • jcdsr
    jcdsr over 4 years
    what about using debounceTime?
  • Günter Zöchbauer
    Günter Zöchbauer over 4 years
    Is a good idea but works better with the other answers with imperative event subscription.
  • jcdsr
    jcdsr over 4 years
    Sure, however, for those who want to know how works HostListener with debounceTime, follow the link below plnkr.co/edit/3J0dcDaLTJBxkzo8Akyg?p=preview
  • Günter Zöchbauer
    Günter Zöchbauer over 4 years
    @jcdsr Nice one!
  • Deepak Thomas
    Deepak Thomas over 4 years
    Where can I add debounce(1000) in this?
  • Chris Stanley over 4 years
    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) })
  • Thanveer Shah over 4 years
    I want to change a varibale from counter = 6 to count = 0 at max-width 767px, How do i do that ?
  • Darren Street over 3 years
    if you want to detect window resizes then Angular Material Breakpoint Observer handles already material.angular.io/cdk/layout/overview
  • Martin Volek
    Martin Volek over 3 years
    But this does not detect only windows resize, but any element resize.
  • DanielWaw over 3 years
    You can add a function to the service and trigger it in component @jcdsr: getScreenSize() { this.onResize$.emit({ width: window.innerWidth, height: window.innerHeight }); }
  • Deniss M.
    Deniss M. about 3 years
    this should be the accepted answer. Has everything needed + this is angular CDK inbuilt functionality.
  • dude
    dude about 3 years
    I'm using @HostListener('window:resize') @throttle(300), but do I need to unsubscribe to the window:resize event and if so, how?
  • Günter Zöchbauer
    Günter Zöchbauer about 3 years
    @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.
  • dude
    dude about 3 years
    @GünterZöchbauer Thanks for your reply. Do you have any reference for this?
  • Totati about 3 years
    @MarkPieszak-Trilon.io It will fire, cause it runs inside the zone. I don't see calling the subscribe inside runOutsideAngular
  • jcdsr
    jcdsr about 3 years
    doesn't work for server-side or in angular universal
  • Totati almost 3 years
    Hi, consider my answer, stackoverflow.com/a/57131782/4797961 debounceTime won't reduce change detection cycles.
  • cgatian
    cgatian almost 3 years
    Sorry I acknowledge that comment may be incorrect and misleading. I don't know about mobile
  • Sean Halls
    Sean Halls over 2 years
    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.
  • oomer
    oomer over 2 years
    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.
  • Günter Zöchbauer
    Günter Zöchbauer over 2 years
    @oomer @HostListene() is an annotation to onResize similar to @Input, @Output(), @ViewChild() @Inject(), ... and annotations always apply to the following element.
  • CRice
    CRice over 2 years
    for debounce, import { debounceTime } from 'rxjs/operators';
  • Jandro Rojas almost 2 years
    this answer needs to be upvoted more. In terms of performance and compatibility, this is the best solution
  • pistol-pete
    pistol-pete almost 2 years
    Tried this and got it working....the performance was drastically slower than options above.
  • Totati over 1 year
    @pistol-pete I've added an example to show how high performant this is.
  • GusSL over 1 year
    Note: addGlobalEventListener is @deprecated as of Angular 12 — No longer being used in Ivy code. To be removed in version 14.
  • GusSL over 1 year
    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()
  • Totati over 1 year
    The global targets has been moved, currently you can find them here: github.com/angular/angular/blob/master/packages/compiler/src‌​/…
  • Totati over 1 year
    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.
  • daniol
    daniol over 1 year
    This requires Angular Material components
  • Totati over 1 year
    No, it requires npmjs.com/package/@angular/cdk, I start my answer with that.
  • Felipe Morais about 1 year
    @daniol add cdk to your project: ng add @angular/cdk