Angular - 创建通用装饰器包装@HostListener

标签 angular typescript decorator

我正在尝试找到一种方法来为我的一些使用 HostListener 的组件制作可重用的装饰器。

我目前有几个功能(组件) 它们非常相似,并且都具有相同的@HostListener block :

@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 方法调用服务中的某个函数(从后端获取数据)并更新局部变量。 相同的服务被注入(inject)到所有组件并且相同的变量在所有组件中可用。 事实上 - 逻辑是精确的,并且在所有这些组件中重复出现。

我想做的是找到一种方法来创建一个自定义装饰器来包装重复的 HostListener,例如:

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

如果需要,我将为这些组件创建一个接口(interface),以声明装饰器要使用的公共(public)服务和局部变量。

关于如何实现此类装饰器的任何想法、指导或帮助?

提前致谢。

最佳答案

不用@HostListner也可以使用自定义装饰器实现

online example

如何实现

  1. 创建一个函数来实现自定义装饰器(WithScrollHostListener)

function WithScrollHostListener() {

  return function decorator(constructor) {
  
     ...
  }
  
 }

  1. 将自定义回调扩展到 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) {
  }
}

关于Angular - 创建通用装饰器包装@HostListener,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59925590/

相关文章:

python - 是我的类内装饰器不够 Pythonic 还是 PyCharm 在 lint 警告方面不够智能?

forms - Angular 形式输入值未定义

angular - 保持垫子菜单打开 Angular

php - Yii2 cors 过滤错误 No 'Access-Control-Allow-Origin' header is present

node.js - typescript 表达 typeorm createconnection

javascript - typescript :合并/覆盖嵌套接口(interface)

angular - 反跳异步验证器

node.js - Jasmine:测试一个方法是否被另一个类的另一个方法调用

JavaScript 装饰器示例不起作用

python - 将装饰器类应用于类方法