Debounce событие @HostListener

Я реализую простую директиву бесконечной прокрутки в Angular2. Я использую @HostListener('window:scroll') чтобы получить событие прокрутки и анализ данных из $target.

Вопрос в том, что для каждого события прокрутки все будет проверено еще раз без необходимости.

Я проверил ионную директиву infinite-scroll для вдохновения, но они не используют @HostListener, им нужен более @HostListener контроль, я думаю.

Я попал в эту проблему во время поиска https://github.com/angular/angular/issues/13248, но не смог найти способ сделать то, что я хочу.

Я думаю, что если я создам Observable, подпишусь на него с помощью debounce и добавлю (следующий) элементы к нему, я достигну желаемого поведения, но я не в состоянии это сделать.

Ответ 1

Я бы использовал декоратор метода 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) {
  ...
}

Пример плунжера

Ответ 2

Мне очень нравится решение @yurzui, и я обновил много кода, чтобы использовать его. Тем не менее, я думаю, что это содержит ошибку. В исходном коде есть только один timeout на класс, но на практике он необходим для каждого экземпляра.

В угловых терминах это означает, что если компонент, в котором используется @debounce() несколько раз в контейнере, то каждое создание будет cancelTimeout время до предыдущего создания и будет 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__.