我想创建一个类装饰器TopicClass
,它为装饰组件添加属性和函数。该函数必须访问注入的服务。 我该怎么做?
以下是尝试失败的原因:
@Component({
selector: 'home',
styleUrls: ['./home.component.css'],
templateUrl: './home.component.html'
})
@TopicClass('home')
export class HomeComponent {
constructor(private topicService: TopicService) { }
}
我无法通过插入的函数ngAfterViewInit
访问注入的服务。
export function TopicClass(title: string) {
return function (target: Function) {
const original = target;
function construct(constructor, args) {
const c: any = function () {
return constructor.apply(this, args);
}
c.prototype = constructor.prototype;
const newInstance = new c();
newInstance['topic'] = new Topic(title, '');
newInstance['ngAfterViewInit'] = () => {
newInstance['topicService'].setTopic(newInstance['topic']);
}
return newInstance;
}
const ctor: any = (...args) => {
console.log("Service: " + original.prototype.topicService);
return construct(original, args);
};
ctor.prototype = original.prototype;
return ctor;
}
}
问题是newInstance['topicService']
未定义。
我已经设置了一个简单的Angular项目进行测试: https://github.com/ptea/angular-class-decorator-test
https://github.com/ptea/angular-class-decorator-test/blob/master/src/app/services/topic.service.ts
我还尝试使用简单的TypeScript程序重现问题,该程序按预期工作:
newInstance['printStreet'] = () => {
console.log(`printFirstnameStreet: ${newInstance['firstname']}, ${newInstance['street']}`);
}
https://github.com/ptea/angular-class-decorator-test/blob/master/dashboard.ts
此问题的任何想法/解决方案?
答案 0 :(得分:1)
TopicService
未定义的原因是因为它从未注入组件。使用当前的装饰器,它将HomeComponent
的构造函数重新定义为不带参数的构造函数。这会弄乱Angular的DI,因此在实例化Component时不会注入TopicService
。
我能想到的最好的方法就是不要修改HomeComponent
的构造函数,而是改为使用ngOnInit
方法。 ngOnInit方法是一个理想的候选方法,因为它按照组件的生命周期调用一次,并且它具有固定数量的参数0,这使得它很容易包装在另一个函数中。您可以使用ngAfterViewInit
函数与此功能相似,以便在需要时使用该服务。
以下是修改TopicClassDecorator
以实现理想结果的方法:
export function TopicClass(title: string) {
return function (target: Function) {
let targetNgOnInit = target.prototype.ngOnInit;
target.prototype.ngOnInit = function (){
this.topic = new Topic(title, 'subTitle');
if(targetNgOnInit){
targetNgOnInit.apply(target);
}
}
let targetNgAfterViewInit = target.prototype.ngAfterViewInit;
target.prototype.ngAfterViewInit = function (){
this.topicService.setTopic(this.topic);
if(targetNgAfterViewInit){
targetNgAfterViewInit.apply(target);
}
}
return target;
}
}
Demo Plunkr所有事情都在一起工作。
答案 1 :(得分:0)
我终于使用了以下解决方案而没有覆盖ngOnInit函数:
export function TopicClass(title: string) {
return function (target: Function) {
target.prototype.topic = new Topic(title);
let targetNgAfterViewInit = target.prototype.ngAfterViewInit;
target.prototype.ngAfterViewInit = function () {
this.topicService.setTopic(this.topic);
if(targetNgAfterViewInit){
targetNgAfterViewInit.apply(target);
}
}
}
}