Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

angular2: How to use observables to debounce window:resize

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

like image 589
LithStud Avatar asked Jul 05 '16 09:07

LithStud


3 Answers

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:

  • https://github.com/angular/angular/issues/4062

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

like image 172
Thierry Templier Avatar answered Dec 12 '22 15:12

Thierry Templier


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

like image 40
Sander Ravenhorst Avatar answered Dec 12 '22 15:12

Sander Ravenhorst


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.

like image 41
Londeren Avatar answered Dec 12 '22 16:12

Londeren