简体   繁体   English

Angular 2 自己的类装饰器来访问依赖注入服务

[英]Angular 2 own Class Decorator to access Dependency Injection Service

I want to create a class decorator TopicClass which adds a property and a function to the decorated component.我想创建一个类装饰器TopicClass ,它向装饰组件添加一个属性和一个函数。 The function must access a injected Service.该函数必须访问注入的服务。 How do I do that?我怎么做?

Here is what tried unsuccessfully:以下是尝试失败的内容:


@Component({
  selector: 'home',
  styleUrls: ['./home.component.css'],
  templateUrl: './home.component.html'
})
@TopicClass('home')
export class HomeComponent {
  constructor(private topicService: TopicService) { }
}

I cannot access the injected Service through the inserted function ngAfterViewInit .我无法通过插入的函数ngAfterViewInit访问注入的 Service。


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;
  }
}

The problem is newInstance['topicService'] is undefined.问题是newInstance['topicService']未定义。

I have set up a simple Angular project for testing: https://github.com/ptea/angular-class-decorator-test我已经建立了一个简单的 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 https://github.com/ptea/angular-class-decorator-test/blob/master/src/app/services/topic.service.ts

I also tried to reproduce the problem with a simple TypeScript program, which works as intended:我还尝试用一个简单的 TypeScript 程序重现这个问题,它按预期工作:


newInstance['printStreet'] = () => {
  console.log(`printFirstnameStreet: ${newInstance['firstname']}, ${newInstance['street']}`);
}

https://github.com/ptea/angular-class-decorator-test/blob/master/dashboard.ts https://github.com/ptea/angular-class-decorator-test/blob/master/dashboard.ts

Any ideas/solution to this problem?这个问题的任何想法/解决方案?

The reason that the TopicService is undefined is because it was never injected into the component. TopicService未定义的原因是因为它从未被注入到组件中。 With your current decorator it is redefining the constructor of the HomeComponent to be a constructor that has no arguments.根据您当前的装饰被重新定义的构造HomeComponent是没有参数的构造函数。 This is messing up Angular's DI and so the TopicService is not injected when the Component is instantiated.这弄乱了 Angular 的 DI,因此在实例化组件时不会注入TopicService

The best way I can think of to accomplish what you want is to not modify the constructor of the HomeComponent and instead tap into the ngOnInit method instead.我能想到的来完成你想要什么,最好的办法是不能修改的构造HomeComponent ,转而进军ngOnInit方法来代替。 The ngOnInit method is an ideal candidate because it is called once per the component's lifecycle and it has a fixed number of parameters, 0, which makes it easy to wrap inside another function. ngOnInit 方法是一个理想的候选方法,因为它在每个组件的生命周期中被调用一次,并且它有固定数量的参数,0,这使得它很容易包装在另一个函数中。 You can similarity do this with the ngAfterViewInit function to use the service when desired.您可以与ngAfterViewInit函数类似地执行此操作,以便在需要时使用该服务。

Here is how you could modify the TopicClassDecorator to acheive your desired result:以下是如何修改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 of everything working together.一切协同工作的演示 Plunkr

I finally went with the following solution without overriding the ngOnInit function:我终于在不覆盖 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);
      }
    }
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM