DEV Community

loading...

Throttling Events in Angular with Rxjs

martinmcwhorter profile image Martin McWhorter Originally published at martinmcwhorter.logdown.com on ・3 min read

Both Angular and Rxjs give you the ability to easily listen to events. Using Angualr's @HostListener() annotation allows a declarative binding while using Rxjs event binding allows for filtering, debouncing and throttling of events, to name a few.

The prefered way to bind to events in an Angular component is to use the @HostListener() annotation. This provides a clean declarative binding from an event to a method, and best of all works without the need to access any browser specific APIs such as document or window. Avoiding direct browser DOM APIs becomes important if you want to use the Universal server-side rendering or run the applicaton in a WebWorker. Keeping your code open to either of these possibilities, even if you have no plan at the moment, allows you to take advantage of future optimizations.

Throttling Resize

You may ask, why do I need to listen to resize events?

You may not need to. As many of your UI elements as possible should respond to form factor changes through CSS cues rather than scripted cues. Mobile and Tablet devices don't have any real stories where the viewport changes sizes that aren't handled by the deviceorientation event. Their are probably some edge cases like split screen and some virtual keyboards pushing up on the browser window.

Their may be some exceptions to this where you will need to script the responsiveness. For example, you may have a menu that is pinned open on desktop form factor, pinned in a minimized mode on tablets in landscape mode, and hidden behind a hamburger menu on portrait tablets and phones.

Even with this scenario a valid situation where a user is resizing, not rotating, the browser window is at best an edge case.

In reality the use case is a developer story. As a developer you want to be sure that your UI behaves at every width between the smallest acceptable width to the largest. You want to be able to stretch your responsive browser developer tools from tiny to extra-large and ensure that the app will display correctly for every pixel width.

With Rxjs we can accomplish very simply:

private resizeObservable = Observable.fromEvent(
    window, 
    'resize'
  ).throttleTime(200);

Nice and clean. But there is a problem with this. We are accessing the DOM window directly. We could wrap this in a try/catch, or inject a window into our component that gets replaced with a fake if we run on the server, but then if we decide to run in a WebWorker we are out of luck and will need to replace this code.

The Solution

We can use the Rjxs throttle method in conjunction with Angular's @HostLister() decorator annotations.

In our example below we create a Subject of type number named resizeSubject. We will call the next() method on resizeSubject in our event handler.

We create an Observable from our Subject and throttle it for 200 miliseconds. Then within our ngOnInit() we will subscribe to our throttled observable.

app.component.ts
export class AppComponent implements OnInit { 

  private resizeSubject = new Subject<number>(); 
  private resizeObservable = this.resizeSubject.asObservable()
    .throttleTime(200); 

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

  ngOnInit() { 
    this.resizeObservable.subscribe(x => 
      this.doSomethingWithThrottledEvent(x)); 
  } 

  private doSomethingWithThrottledEvent(width: number) { 
    // . . . 
  } 
}

Discussion (0)

pic
Editor guide