27

so i am trying to figure out a way to debouce window:resize events using observables, so some kind of function would be called only after user stoped resizing window or some time has passed without size change (say 1sec).

https://plnkr.co/edit/cGA97v08rpc7lAgitCOd

import {Component} from '@angular/core'

@Component({
  selector: 'my-app',
  providers: [],
  template: `
    <div (window:resize)="doSmth($event)">
      <h2>Resize window to get number: {{size}}</h2>

    </div>
  `,
  directives: []
})
export class App {
  size: number;
  constructor() {
  }

  doSmth(e: Event) {
    this.size = e.target.innerWidth;
  }
}

is just a simple sample that uses window:resize and shows that it reacts instantly (use "Launch preview in separate window").

LithStud
  • 464
  • 2
  • 6
  • 16

4 Answers4

50

I think that you can't debounce this way using observable. In fact, such things aren't supported out of the box right now but there is an open issue for this:

To achieve your goal, you could use directly the Observable.fromEvent to get an observable for this event. So you can apply the debounceTime operator on this observable.

Here is a sample:

@Component({
  (...)
})
export class App {
  size: number;
  constructor() {
    Observable.fromEvent(window, 'resize')
        .debounceTime(1500)
        .subscribe((event) => {
          this.doSmth(event);
        });
  }

  doSmth(e: Event) {
    console.log('do smth');
    this.size = e.target.innerWidth;
  }
}

See this plunkr: https://plnkr.co/edit/uVrRXtnZj8warQ3qUTdN?p=preview

Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
  • 1
    You're welcome! Perhaps this article could interest you: https://gist.github.com/staltz/868e7e9bc2a7b8c1f754 ;-) – Thierry Templier Jul 05 '16 at 09:46
  • 1
    It seems like this is now the current bug/issue for this feature: https://github.com/angular/angular/issues/13248 – Stephen Kaiser Jul 06 '17 at 16:14
  • 1
    Modern version: ngOnInit(): void { this.alive = true; fromEvent(window, 'resize') .pipe( takeWhile(() => this.alive), debounceTime(500) ) .subscribe((event) => { this.gridApi?.sizeColumnsToFit(); }); } – Marc Oct 15 '21 at 14:43
12

In one of our apps we also had the implementation Thierry Templier proposes, but I noticed that Angular's change detection is firing (a lot) on window resize, which makes our app slow on resize.

Fixed it by using zones & Subject, like so:

private changeSubject = new Subject<number>();

constructor(private zone: NgZone) {
  this.zone.runOutsideAngular(() => {
    Observable.fromEvent(window, 'resize')
    .debounceTime(1500).distinctUntilChanged().subscribe((e: Event) => {
      this.zone.run(() => {
        this.changeSubject.next(e);
      })
    }
    )
  });
  this.changeSubject.subscribe((e: Event) => { this.doSmth(e); });
}

See plunker with the issue here (resize screen and watch console).

And plunker with the fix for this issue here (resize screen and watch console).

isherwood
  • 58,414
  • 16
  • 114
  • 157
11

You can use @HostListener decorator. This is a common way to subscribe events like this.

@Component({
   // ...
})
export class App {
  private changeSize = new Subject();

  constructor() {
    this.changeSize
    .asObservable()
    .pipe(
      throttleTime(1000)
    )
    .subscribe(innerWidth => console.log('innerWidth:', innerWidth));
  }

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

You can read more about @HostListener here or here.

Londeren
  • 3,202
  • 25
  • 26
2
import { Component, OnInit, HostListener } from '@angular/core';
import { Subject } from 'rxjs';
import 'rxjs/add/operator/debounceTime';

@Component({
   // ...
})
export class App implements OnInit {
  resize$ = new Subject<void>();

  ngOnInit() {
    this.resize$.debounceTime(300).subscribe(
      innerWidth => console.log('innerWidth:', innerWidth)
    );
  }

  @HostListener('window:resize', ['$event.target'])
  onResize(target) {
    this.resize$.next(target.innerWidth);
  }
}
A. Morel
  • 9,210
  • 4
  • 56
  • 45