简体   繁体   中英

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. 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 .


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.

I have set up a simple Angular project for testing: https://github.com/ptea/angular-class-decorator-test

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:


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

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. With your current decorator it is redefining the constructor of the HomeComponent to be a constructor that has no arguments. This is messing up Angular's DI and so the TopicService is not injected when the Component is instantiated.

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. 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. You can similarity do this with the ngAfterViewInit function to use the service when desired.

Here is how you could modify the TopicClassDecorator to acheive your desired result:

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.

I finally went with the following solution without overriding the ngOnInit function:


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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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