Дебаунсинг компонентных методов в Angular

Использование методов компонентов обычно неразумно по известным причинам, но иногда в приложении могут быть сторонние компоненты, которые требуют использования методов компонента в шаблоне для реакции на выход.

В сегодняшней статье мы рассмотрим способ дебаггинга выполнения методов компонента, чтобы они не срабатывали слишком часто, не обращаясь к инструментам rxjs.

Для простоты мы будем использовать простой компонент, реализующий счетчик.

import { Component } from '@angular/core';

@Component({
  template: `<p>{{ calledTimes }}</p>
    <p><button (click)="increment()">increment</button></p>`,
  selector: 'app-root',
})
export class AppComponent {
  private _calledTimes = 0;

  public get calledTimes(): number {
    return this._calledTimes;
  }

  public increment(): void {
    this._calledTimes++;
  }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Все довольно просто: есть свойство, которое увеличивается каждый раз, когда вызывается обработчик в шаблоне. Для демонстрации мы могли бы использовать rxjs’ Subjects с debounceTime, но такое решение будет ограничено компонентом, который его реализует, и не может быть перенесено на другие компоненты, которым может понадобиться дебаунсинг вызовов их методов.

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

export function debounce(ms: number) {
  return (
    target: unknown,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ): void => {
    let timer: ReturnType<typeof setTimeout>;
    const originalMethod = descriptor.value;

    descriptor.value = function (...args: any[]) {
      new Promise((resolve) => {
        if (timer) {
          clearTimeout(timer);
        }

        timer = setTimeout(() => {
          resolve(originalMethod.apply(this, ...args));
        }, ms);
      });
    };
  };
}
Вход в полноэкранный режим Выход из полноэкранного режима

Этот подход хорош тем, что теперь у нас есть отдельный механизм дебаггинга, который мы можем применить к любому методу любого компонента. Так, если мы хотим ограничить инкремент до 2 секунд, мы можем просто украсить метод:

import { Component } from '@angular/core';

import { debounce } from './debounce/debounce.decorator';

@Component({
  template: `<p>{{ calledTimes }}</p>
    <p><button (click)="increment()">increment</button></p>`,
  selector: 'app-root',
})
export class AppComponent {
  private _calledTimes = 0;

  public get calledTimes(): number {
    return this._calledTimes;
  }

  @debounce(2000)
  public increment(): void {
    this._calledTimes++;
  }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Все просто 🙂

Оцените статью
devanswers.ru
Добавить комментарий