获取没有构造函数注入的服务实例

时间:2016-05-27 11:34:19

标签: angular angular2-di angular2-injection

我在bootstrap中定义了@Injectable服务。我想在不使用构造函数注入的情况下获取服务的实例。我尝试使用ReflectiveInjector.resolveAndCreate但似乎创建了一个新实例。

我试图做的原因是我有一个由许多组件派生的基本组件。现在我需要访问服务,但我不想将它添加到ctor,因为我不想在所有派生组件上注入服务。

TLDR:我需要ServiceLocator.GetInstance<T>()

更新:RC5 +的更新代码:Storing injector instance for use in components

5 个答案:

答案 0 :(得分:49)

是的,ReflectiveInjector.resolveAndCreate()创建一个新的未连接的注入器实例。

您可以使用

注入Angulars Injector实例并从中获取所需的实例
constructor(private injector:Injector) {
  injector.get(MyService);
}

您还可以将Injector存储在某个全局变量中,然后使用此注入器实例来获取提供的实例,例如https://github.com/angular/angular/issues/4112#issuecomment-153811572中所述

答案 1 :(得分:21)

在使用ngModules的更新的Angular中,您可以在代码中的任何位置创建可用的变量:

export let AppInjector: Injector;

export class AppModule {
  constructor(private injector: Injector) {
    AppInjector = this.injector;
  }
}

答案 2 :(得分:5)

另一种方法是定义一个自定义装饰器(CustomInjectable来设置依赖注入的元数据:

export function CustomComponent(annotation: any) {
  return function (target: Function) {

    // DI configuration
    var parentTarget = Object.getPrototypeOf(target.prototype).constructor;
    var parentAnnotations = Reflect.getMetadata('design:paramtypes', parentTarget);

    Reflect.defineMetadata('design:paramtypes', parentAnnotations, target);

    // Component annotations / metadata
    var annotations = Reflect.getOwnMetadata('annotations', target);
    annotations = annotations || [];
    annotations.push(annotation);
    Reflect.defineMetadata('annotations', annotations, target);
  }
}

它将利用父构造函数中的元数据而不是自己的元数据。您可以在子类上使用它:

@Injectable()
export class SomeService {
  constructor(protected http:Http) {
  }
}

@Component()
export class BaseComponent {
  constructor(private service:SomeService) {
  }
}

@CustomComponent({
  (...)
})
export class TestComponent extends BaseComponent {
  constructor() {
    super(arguments);
  }

  test() {
    console.log('http = '+this.http);
  }
}

有关详细信息,请参阅此问题:

答案 3 :(得分:0)

在遇到这个问题几次后,我设计了一个很好的方法来克服它,通过在 Angular Injector 服务中使用 getter,而不是直接将服务注入构造函数中。这允许在被引用之前构造服务时间。我的示例仅使用服务,但同样的事情可以应用于使用服务的组件,只需将 getter 放在组件中而不是示例中的 BService

我所做的是使用 getter 将服务注入使用 Injector 类的类属性,如果类属性之前尚未设置,则该服务仅注入一次(第一次调用吸气剂)。这允许服务的使用方式与在构造函数中注入的方式基本相同,但不会出现循环引用错误。只需使用吸气剂 this.aService。只有当这不起作用时,如果您尝试在 AService 的构造函数中使用 Bservice,那么您将遇到相同的循环引用问题,因为 Aservice 不会准备好了吗。 通过使用 getter,您可以推迟注入服务,直到您需要它。

有一些论点,AService 依赖于 BService,而 BService 依赖于 AService,是不好的形式,但每个规则都有例外,每种情况都不同,所以在我看来,这是处理这个问题的一种简单有效的方法。

// a.service.ts
import { Injectable } from '@angular/core';

import { BService } from './b.service';

@Injectable({
  providedIn: 'root'
})
export class AService {

  constructor(
    private bService: BService,
  ) { }

  public foo() {
    console.log('foo function in AService!');
    this.bService.bar();
  }
}
// b.service.ts
import { Injectable, Injector } from '@angular/core';

import { AService } from './a.service';


@Injectable({
  providedIn: 'root'
})
export class BService {
  // Use the getter 'aService' to use 'AService', not this variable.
  private _aService: AService;

  constructor(
    private _injector: Injector,
  ) { }

  // Use this getter to use 'AService' NOT the _aService variable.
  get aService(): AService {
    if (!this._aService) {
      this._aService = this._injector.get(AService);
    }
    return this._aService;
  }

  public bar() {
    console.log('bar function in BService!');
    this.aService.foo();
  }
}

答案 4 :(得分:-1)

StoreService .ts

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

    @Injectable()
    export class StoreService {
      static isCreating: boolean = false;
      static instance: StoreService ;

      static getInstance() {
        if (StoreService.instance == null) {
          StoreService.isCreating = true;
          StoreService.instance = new StoreService ();
          StoreService.isCreating = false;
        }
        return StoreService.instance;
      }
      constructor() {
        if (!StoreService.isCreating) {
          throw new Error('You can\'t call new in Singleton instances! Call StoreService.getInstance() instead.');
        }
     }

  MyAlertMethod(){
    alert('hi);
    }
  }

component.ts

//call this service directly in component.ts as below:-

 StoreService.getInstance().MyAlertMethod();