简体   繁体   中英

Angular 2 - Inject a dependency into a decorator factory

Is there a way to inject a dependency into a decorator factory, using Angular's DI? Let's take the following code as a simplified example:

@Component({
  selector: 'hello-component',
  template: '<div>Hello World</div>'
})
export class HelloComponent {
  @PersonName()
  name: string;

  ngAfterViewInit() {
    console.log(`Hello, ${this.name}`);
  }
}

Here, the intended behaviour of the PersonName decorator is for it to access a Person dependency, and use it to set the name property of the class.

Is it possible at all to implement the PersonName decorator for the code above?

Currently, to inject dependencies into my decorators (and other extra module classes) I'm using this workaround:

import {Injectable, Injector} from "@angular/core";
@Injectable()
export class ExtraModuleInjector {
  private static injector;

  public static get(token: any) {
    if (ExtraModuleInjector.injector) {
      return ExtraModuleInjector.injector.get(token);
    }
  }

  constructor(public injector: Injector) {
    ExtraModuleInjector.injector = injector;
  }
}

After being injected to root component, it allows to use static get method to get dependencies during runtime functions execution. Still looking for better solution.

It's a bit tricky to do this, because decorators are executed at build time, not at the runtime. When decorator is executed, there's no instance of the class.

Back in ng2.beta.10, I used this to get the data from service (don't think you can from component, but I could be wrong...):

// some-route.ts
@CanActivate((next, prev) => {
  let store: any = getSingleton(Store);
})

// injector.ts
import {Injector} from 'angular2/core'

let appInjectorRef: Injector;

export const appInjector = (injector?: Injector) => {
  if (injector)
    appInjectorRef = injector;
  return appInjectorRef;
}

export function getSingleton(token: any) {
  let injector: Injector = appInjector();
  return injector.get(token);
}

..to be honest, looking at this code now I have no ide how it works (; But I know it did back then. Not sure what's the status now, or if there were any breaking changes since beta.10 related to the Injector and ApplicationRef ...

One way to do it in Angular 7 is:

app.module.ts

import { setInjector } from './service/inj.service';
@NgModule({..})
export class AppModule {
  constructor(i: Injector) {
    setInjector(i)
  }
}

decorator.service.ts

import { Injectable, Injector } from '@angular/core';

let injector: Injector;
export const setInjector = (i: Injector)=>{
  injector = i
}

export function mydecorator(arg:any){
  let service: SomeServiceWhichNeedsToBeInjected
  return (target, key, descriptor)=>{
    if(descriptor === undefined) {
      descriptor = Object.getOwnPropertyDescriptor(target, key)
    }
    var originalMethod = descriptor.value
    descriptor.value = function () {
      if(!loading){
        service= injector.get(SomeServiceWhichNeedsToBeInjected)
      }
      //do something with service
      return originalMethod.apply(this, arguments)
    }
    return descriptor;
  }
}

other.service.ts

@mydecorator('somearg')
decoratedmethod(arg1){
  //..
}

You can do following:

export class AnnotationExample {
  @PersonName
  name: string;

  constructor() {
    alert(this.name);
  }
}

function PersonName(/*target: Function, fnName: string, fn: any*/) {
  return {value: 'amit'};
}

var whatIs = new AnnotationExample();

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