Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Debounce @HostListener event

Tags:

angular

rxjs

I'm implementing a simple infinite-scroll directive in Angular2. I'm using @HostListener('window:scroll') to get the scroll event and parsing the data from the $target.

The question is, for every scroll event, everything will be checked once again with no need.

I checked the ionic infinite-scroll directive for inspiration but they don't use @HostListener, they need a more granular control, I guess.

I ended up on this issue while searching https://github.com/angular/angular/issues/13248 but couldn't find any way to do what I want.

I think if I create an Observable, subscribe to it with debounce and push (next) items to it, I will reach the behaviour I want, but I'm not being able to do that.

like image 663
Victor Ivens Avatar asked Jun 19 '17 15:06

Victor Ivens


2 Answers

I would leverage debounce method decorator like:

export function debounce(delay: number = 300): MethodDecorator {   return function (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) {     const timeoutKey = Symbol();      const original = descriptor.value;      descriptor.value = function (...args) {       clearTimeout(this[timeoutKey]);       this[timeoutKey] = setTimeout(() => original.apply(this, args), delay);     };      return descriptor;   }; } 

and use it as follows:

@HostListener('window:scroll', ['$event'])   @debounce()  scroll(event) {   ... } 

Ng-run Example

like image 119
yurzui Avatar answered Oct 02 '22 16:10

yurzui


I really like @yurzui's solution and I updated a lot of code to use it. However, I think it contains a bug. In the original code, there is only one timeout per class but in practice one is needed per instance.

In Angular terms, this means that if the component in which @debounce() is used is instantiated multiple times in a container, every instantiation will cancelTimeout the previous instantiation and only the last will fire.

I propose this slight variant to eliminate this trouble:

export function debounce(delay: number = 300): MethodDecorator {   return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {      const original = descriptor.value;     const key = `__timeout__${propertyKey}`;      descriptor.value = function (...args) {       clearTimeout(this[key]);       this[key] = setTimeout(() => original.apply(this, args), delay);     };      return descriptor;   }; } 

Of course, it is possible to be more sophisticated about disambiguating the synthetic __timeout__ property.

like image 40
Mark Florence Avatar answered Oct 02 '22 17:10

Mark Florence