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

Posted

技术标签:

【中文标题】Angular - 创建通用装饰器包装 @HostListener【英文标题】:Angular - Create common decorator wrapping @HostListener 【发布时间】:2020-05-12 12:01:57 【问题描述】:

我正在尝试为我的一些使用 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 方法正在调用服务中的某个函数(从后端获取数据)并更新局部变量。 将相同的服务注入所有组件,并且所有组件都可以使用相同的变量。 事实上 - 逻辑是精确的并且在所有这些组件中重复。

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

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

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

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

提前致谢。

【问题讨论】:

【参考方案1】:

你可以使用自定义装饰器在没有@HostListner 的情况下实现它

online example

如何实现

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

function WithScrollHostListener() 

  return function decorator(constructor) 
  
     ...
  
  
 
    将自定义回调扩展到角钩(参见装饰器函数)

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) 
  

【讨论】:

这对常春藤有效吗? github.com/angular/angular/issues/16023

以上是关于Angular - 创建通用装饰器包装 @HostListener的主要内容,如果未能解决你的问题,请参考以下文章

Angular `@Host` 装饰器没有到达顶部?

用于包装尝试的通用装饰器除了在 python 中?

typescript Angular 2应用程序角色访问装饰器,包装内置CanAccess功能。当用户角色不是ap时,防止视图转换

使用类装饰器时需要 Angular2 反射元数据填充程序

可以创建一个知道方法对象的@synchronized 装饰器吗?

如何为现有的 Javascript 库创建 Angular 库包装器?