我在Bootstrap中定义了一个@Injectable
服务。我想在不使用构造函数注入的情况下获得服务的实例。我尝试使用ReflectiveInjector.resolveAndCreate
,但这似乎创建了一个新实例。
我尝试这样做的原因是我有一个从许多组件派生而来的基本组件。现在我需要访问一个服务,但我不想将其添加到构造函数中,因为我不想在所有派生组件上注入该服务。
TLDR:我需要ServiceLocator.GetInstance<T>()
更新:RC5+的更新代码:存储注射器实例以用于组件
在使用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);
ReflectiveInjector.resolveAndCreate()
创建了一个新的未连接的注入器实例。
您可以注入AngularsInjector
实例,并使用从中获得所需的实例
constructor(private injector:Injector) {
injector.get(MyService);
}
您还可以将Injector
存储在某个全局变量中,然后使用此注入器实例来获取所提供的实例,例如https://github.com/angular/angular/issues/4112#issuecomment-153811572
另一种方法是定义一个自定义装饰器(一个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);
}
}
有关更多详细信息,请参阅此问题:
- Angular2使用从基类导入的库
inject
函数。
根据文件
在实践中,在构造函数、构造函数参数和字段初始值设定项中允许inject()调用:
@Injectable({providedIn: 'root'})
export class Car {
radio: Radio|undefined;
// OK: field initializer
spareTyre = inject(Tyre);
constructor() {
// OK: constructor body
this.radio = inject(Radio);
}
}
和
从提供商的工厂调用注入也是合法的:
providers: [
{provide: Car, useFactory: () => {
// OK: a class factory
const engine = inject(Engine);
return new Car(engine);
}}
]
因此,在您的情况下,您可以使用
class MyClass {
serviceLocator = inject(ServiceLocator)
}
在遇到这个问题几次后,我设计了一个很好的方法来克服它,方法是在AngularInjector
服务中使用getter,而不是直接在构造函数中注入服务。这允许在引用之前构造服务时间。我的例子只使用服务,但同样的东西也可以应用于使用服务的组件,只需将getter放在组件中,而不是放在例子中的BService
中。
我所做的是使用getter使用Injector
类将服务注入到类属性中,如果以前没有设置类属性,那么服务只注入一次(第一次调用getter)。这允许以与在构造函数中注入服务基本相同的方式使用服务,但不会出现循环引用错误。只需使用getterthis.aService
。他们唯一不起作用的时候是,如果你试图在Bservice
的构造函数中使用AService
,那么你会遇到同样的循环引用问题,因为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();
}
}
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);
}
}
.ts
//call this service directly in .ts as below:-
StoreService.getInstance().MyAlertMethod();