Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 592
Merott Avatar asked Jun 19 '16 11:06

Merott


3 Answers

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.

like image 145
lexigren Avatar answered Nov 14 '22 04:11

lexigren


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

like image 5
Sasxa Avatar answered Nov 14 '22 04:11

Sasxa


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){
  //..
}
like image 1
Anand Rockzz Avatar answered Nov 14 '22 05:11

Anand Rockzz