我正在尝试找到一种方法来为我的一些使用 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也可以使用自定义装饰器实现
如何实现
- 创建一个函数来实现自定义装饰器(
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) {
}
}
关于Angular - 创建通用装饰器包装@HostListener,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59925590/