【问题标题】:Angular - Create common decorator wrapping @HostListenerAngular - 创建通用装饰器包装 @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 {
}

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

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

提前致谢。

【问题讨论】:

    标签: angular typescript decorator


    【解决方案1】:

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

    online example

    如何实现

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

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

    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) {
      }
    }

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-02-13
    • 2017-11-16
    • 1970-01-01
    • 2017-02-04
    • 2021-05-20
    • 1970-01-01
    • 2023-04-03
    • 1970-01-01
    相关资源
    最近更新 更多