HostListener + Output = 🤔

HostListener позволяет нам слушать события.

Output испускает наблюдаемые события.

Можно ли использовать их вместе? Да, можно! (открыть в Stackblitz)

@Component({
   selector: "my-counter",
   template: `
      Counter: {{ count }}
   `,
   changeDetection: ChangeDetectionStrategy.OnPush,
})
export class Counter {
   @Input() count = 0

   @Output() 
   readonly increment = interval(1000)

   @HostListener("increment", ["$event"])
   handleIncrement(increment) {
      this.count += increment
  }
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Преднамеренное поведение? Возможно, нет!

Было немного удивительно узнать, что компоненты и директивы Angular могут слушать пользовательские события, публикуемые декоратором Output. Это дает нам удобный способ подписаться на наблюдаемые события без последующей очистки; подписка автоматически удаляется при уничтожении компонента. Однако при этом мы загрязняем пространство имен output 🙆♂️.

Также нет необходимости в share, поскольку выходные данные автоматически рассылаются подписчикам родительского шаблона. Это даже работает с OnPush. Что еще мы можем сделать?

Пример с HTTP-запросом

Давайте получим несколько тодосов.

const handleError = ctx => source => source.pipe(
   catchError((error, source) =>
      ctx.handleError(error) ? source : EMPTY
   )
)

@Component({
   template: `
      I have {{ todos.length }} todos left to do
   `
})
export class Counter {
   protected todos = []

   @Output() 
   readonly response = inject(HttpClient)
      .get("https://jsonplaceholder.typicode.com/todos")
      .pipe(handleError(this))

   @HostListener("response", ["$event"])
   handleResponse(todos) {
      this.todos = todos
   }

   handleError(error: unknown) {
      console.error(error)
      return true
   }
}
Войти в полноэкранный режим Выход из полноэкранного режима

Поскольку выходы Angular доступны только для чтения, новые значения не могут быть присвоены, как AsyncPipe. Вместо этого нам нужно обрабатывать ошибки, чтобы предотвратить разрушение подписки.

Другие вещи, которые вы не можете сделать

  • Событие listen в Renderer2, к сожалению, не имеет такого поведения.
  • Подписка на одно и то же событие дважды с помощью HostListener невозможна, будет использоваться только последняя подписка.
  • Это не будет работать в AppComponent, поскольку нет родительского представления для Output, на которое можно подписаться.

Резюме

Это просто небольшой трюк, позволяющий управлять подпиской без использования сторонних библиотек. Надеюсь, вы найдете его полезным.

Счастливого кодинга!

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