Angular - Создать общий декоратор-обертку @HostListener - PullRequest
2 голосов
/ 27 января 2020

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

В настоящее время у меня есть несколько функций (компонентов), которые очень похожи и имеют тот же блок @HostListener:

@Component({...})
export class MySearchComponent implements OnInit, OnDestroy {

   @HostListener('window:scroll', [])
   onScroll(): void {
      this.loading = true;
      this.commonService.getData(
         this.tab,
         this.query,
         ...
      ).subscribe(results => {
         this.results = results;
         this.loading = false;
      })
   }

}

Метод HostListener вызывает некоторую функцию в службе (для получения данных из серверной части) и обновляет локальные переменные. Один и тот же сервис внедряется во все компоненты, и во всех них доступны одни и те же переменные. Infact - logi c является EXACT и повторяется во всех этих компонентах.

Я хотел бы найти способ создать собственный декоратор, который будет оборачивать повторяющийся HostListener, такой как:

@Component({...})
@WithScrollHostListener()
export class MySearchComponent implements OnInit, OnDestroy {
}

При необходимости я создам интерфейс для этих компонентов для объявления общей службы и локальной переменной, которая будет использоваться декоратором.

Любая идея, руководство или помощь в отношении того, как реализовать такой декоратор?

Заранее спасибо.

1 Ответ

1 голос
/ 27 января 2020

Вы можете использовать собственный декоратор для его реализации без @ HostListner

онлайн-пример

Как его реализовать

  1. создать функцию для реализации пользовательского декоратора (WithScrollHostListener)

function WithScrollHostListener() {

  return function decorator(constructor) {
  
     ...
  }
  
 }
расширение пользовательского обратного вызова в angular hook (см. Функцию декоратора)

function WithScrollHostListener() {

  // required
  function extendHook(arg: {
    hookName: string;
    target: {
      prototype;
    };
    fn: (hookArg: { componentInstance }) => void;
  }) {
    const original = arg.target.prototype[arg.hookName];

    arg.target.prototype[arg.hookName] = function(...args) {
      arg.fn({
        componentInstance: this
      });
      original && original.apply(this, args);
    };
  }
 
  // required
  return function decorator(constructor) {
    extendHook({
      // hook's name according to you (e.x. ngOnInit , ngAfterViewInit)
      hookName: "ngOnInit",
      target: constructor,
      // setup your custom logic
      fn: hookArg => {
        window.addEventListener("scroll", () =>
          scrollFn({
            commonComponent: hookArg.componentInstance
          })
        );
      }
    });
  };
  
 }

Полные коды

import { Component, Injectable } from "@angular/core";
import { CommonService } from "./common.service";

// optional (shared the same structure with other component)
export interface CommonComponent {
  loading?;
  tab?;
  query?;
  results?;

  commonService?: CommonService;
}

function WithScrollHostListener() {
  // custom logic
  function scrollFn(arg: { commonComponent: CommonComponent }) {
    arg.commonComponent.loading = true;
    arg.commonComponent.commonService
      .getData(arg.commonComponent.tab, arg.commonComponent.query)
      .subscribe(results => {
        console.log(results);
        arg.commonComponent.results = results;
        arg.commonComponent.loading = false;
      });
  }

  // required
  function extendHook(arg: {
    hookName: string;
    target: {
      prototype;
    };
    fn: (hookArg: { componentInstance }) => void;
  }) {
    const original = arg.target.prototype[arg.hookName];

    arg.target.prototype[arg.hookName] = function(...args) {
      arg.fn({
        componentInstance: this
      });
      original && original.apply(this, args);
    };
  }
 
  // required
  return function decorator(constructor) {
    extendHook({
      // hook's name according to you (e.x. ngOnInit , ngAfterViewInit)
      hookName: "ngOnInit",
      target: constructor,
      // setup your custom logic
      fn: hookArg => {
        window.addEventListener("scroll", () =>
          scrollFn({
            commonComponent: hookArg.componentInstance
          })
        );
      }
    });
  };
}

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
@WithScrollHostListener()
export class AppComponent implements CommonComponent {
  constructor(public commonService: CommonService) {
  }
}
...