Angular 2拥有类装饰器来访问依赖注入服务

时间:2017-01-05 16:53:18

标签: javascript angular typescript

我想创建一个类装饰器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

此问题的任何想法/解决方案?

2 个答案:

答案 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);
      }
    }
  }
}
相关问题