109

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

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

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

更新:更新了 RC5+ 的代码:存储注入器实例以在组件中使用

4

5 回答 5

88

在使用 ngModules 的更新后的 Angular 中,您可以在代码的任何位置创建一个可用的变量:在app.module.ts
中添加此代码

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


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

现在,您可以使用AppInjector来在代码的任何位置查找任何服务。

import {AppInjector} from '../app.module';

const myService = AppInjector.get(MyService);
于 2017-03-14T12:32:22.013 回答
87

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

您可以使用注入 AngularsInjector实例并从中获取所需的实例

constructor(private injector:Injector) {
  injector.get(MyService);
}

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

于 2016-05-27T11:42:33.883 回答
7

另一种方法包括定义一个自定义装饰器(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);
  }
}

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

于 2016-05-27T12:12:35.747 回答
1

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

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

有一些论点认为,AService取决于BServiceBService取决于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();
  }
}
于 2021-02-22T12:29:45.910 回答
-1

存储服务 .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);
    }
  }

.ts

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

 StoreService.getInstance().MyAlertMethod();
于 2020-04-28T07:36:40.473 回答