去抖动@HostListener事件

时间:2017-06-19 15:53:14

标签: angular rxjs

我在Angular2中实现了一个简单的无限滚动指令。 我使用@HostListener('window:scroll')来获取滚动事件并解析$target中的数据。

问题是,对于每个滚动事件,都会再次检查所有内容而不需要。

我检查了离子infinite-scroll指令的灵感,但他们没有使用@HostListener,我猜他们需要更精细的控制。

我在搜索https://github.com/angular/angular/issues/13248时最终遇到了这个问题,但无法找到任何方法来做我想要的事情。

我认为如果我创建一个Observable,使用debounce和push(next)项目订阅它,我会达到我想要的行为,但我无法做到。

3 个答案:

答案 0 :(得分:38)

我会利用debounce方法装饰器,如:

export function debounce(delay: number = 300): MethodDecorator {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    let timeout = null

    const original = descriptor.value;

    descriptor.value = function (...args) {
      clearTimeout(timeout);
      timeout = setTimeout(() => original.apply(this, args), delay);
    };

    return descriptor;
  };
}

并按如下方式使用:

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

<强> Plunker Example

答案 1 :(得分:4)

我真的很喜欢@yurzui的解决方案,并且我更新了很多代码来使用它。但是,我认为它包含一个错误。在原始代码中,每个只有一个timeout,但实际上每个 instance 都需要一个。

用Angular术语来说,这意味着如果在容器中实例化了使用@debounce()的组件多次,则每个实例化将cancelTimeout前一个实例化,而只有最后一个实例将触发。

我建议使用这种轻微的变体来消除这种麻烦:

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

当然,对于消除合成的__timeout__属性的歧义可能会更加复杂。

答案 2 :(得分:1)

使用 fromEventthrottleTime 运算符可以实现执行此操作的 RXJS 方式。

不是使用 @HostListener 装饰您的事件处理程序,而是使用 fromEvent(例如,在 ngOnInit 方法中)从事件创建可观察对象,然后使用 { {1}}。

throttleTime

使用 RXJS 的一个优点是您可以将自定义调度程序传递给 ... import {fromEvent, Subscription} from 'rxjs'; import {tap, throttleTime} from 'rxjs/operators'; export class MyComponent implements OnInit, OnDestroy { private eventSub: Subscription; ngOnInit() { this.eventSub = fromEvent(window, 'scroll').pipe( throttleTime(300), // emits once, then ignores subsequent emissions for 300ms, repeat... tap(event => this.scroll(event)) ).subscribe(); } scroll(event) { ... } ngOnDestroy() { this.eventSub.unsubscribe(); // don't forget to unsubscribe } } 运算符以实现不同的行为。例如,您可以按动画帧速率限制事件发射(例如,限制触摸事件的发射)。

throttleTime