在我的Angular 2应用程序中,我有一个要求,我需要在运行时为给定的基本类型注册两种派生类型之一。我想出了这个:
在我的模块中,我使用了Provider Factory。 ApiService的构造函数需要角度Http服务和我自己的ConfigService,这就是deps属性的用途。
// imports omitted
let appContentBranchValue = 'myApp';
@NgModule({
bootstrap: [AppComponent],
declarations: [
AppComponent
],
imports: [ ... ],
providers: [
{ provide: 'AppContentBranch', useValue: appContentBranchValue },
{ provide: ApiService,
deps: [Http, ConfigService],
useFactory: ApiServiceProviderFactory.create(appContentBranchValue) },
]
})
export class MyAccountModule { }
在我的TypeScript服务类中,在定义ApiService及其派生类型之前,我有这个Provider Factory实现。
我把实现放在这里主要是为了保持如何在模块之间选择ApiServiceMock和ApiServiceReal的逻辑。如果某个localStorage键存在且具有真值,则工厂将创建并返回ApiServiceMock,否则返回ApiServiceReal。
export class ApiServiceProviderFactory {
public static create = (appContentBranch: string) => {
let factory = (http: Http, configService: ConfigService): ApiService => {
let flagKey = appContentBranch + '.mock.api';
let flagValue = localStorage.getItem(flagKey);
if (flagValue && JSON.parse(flagValue)) {
return new ApiServiceMock(http, configService);
} else {
return new ApiServiceReal(http, configService);
}
};
return factory;
};
}
不是将Http和ConfigService传递给我的工厂函数,而是可以改为像这样传递Injector吗?
let factory = (injector: InjectorOfSomeKind): ApiService => {
let flagKey = appContentBranch + '.mock.api';
let flagValue = localStorage.getItem(flagKey);
if (flagValue && JSON.parse(flagValue)) {
return injector.get(ApiServiceMock);
} else {
return injector.get(ApiServiceReal);
}
};
return factory;
};
}
如果我能做到这一点,那么可以完全避免如何调用构造函数的脏事。
可以这样做吗?问候,伊恩。
答案 0 :(得分:0)
只需注入injector:Injector
,然后使用injector.get(ApiService)
。
测试时可以使用
TestBed.configureTestingModule({
providers: [{provide: ApiService, useClass: ApiServiceMock}]
});
答案 1 :(得分:0)
我遇到了类似的需求,并且能够将dejector依赖性作为deps的一部分注入
const cookieServiceFactory = (platformId: object, injector: Injector) => {
return isPlatformBrowser(platformId) ? injector.get(BrowserCookieService) : injector.get(ServerCookieService);};
并注册如下
providers:[{provide:'cookieFactory', useFacory: cookieServiceFactory, deps:[PLATFORM_ID, Injector]}]
答案 2 :(得分:0)
如果我的理解正确,您想在运行时注入一个不同的ApiService
实例,具体取决于所检索的本地存储键的值,该名称基于恒定值。
要根据您期望的标准注入正确的实现,请创建工厂函数并将所需的依赖项指定为函数参数。然后在模块定义中,在声明提供程序时使用deps
属性,并将依赖项传递给函数参数。
就是这样!现在,您的工厂功能仅需要根据您的条件返回正确的实现。
MyAccountModule
const appContentBranch = 'myApp';
function apiServiceFactory(http: HttpClient, configService: ConfigService): ApiService {
// retrieve key from LocalStorage
const useMockApi = localStorage.getItem(`${appContentBranch}.mock.api`);
// if key is present and its value is true
if (useMockApi && JSON.parse(useMockApi)) {
return new ApiServiceMock(http, configService);
}
// otherwise return real api
return new ApiServiceReal(http, configService);
}
@NgModule({
imports: [
HttpClientModule,
]
providers: [
ConfigService,
{
provide: ApiService,
useFactory: apiServiceFactory,
deps: [HttpClient, ConfigService],
},
]
})
export class MyAccountModule { }
ApiService
export abstract class ApiService {
// define methods to implement
// it needs to be an abstract class instead of an interface
// so that it can be used as an injection token
}
ApiServiceMock
@Injectable()
export class ApiServiceMock implements ApiService {
constructor(
private http: HttpClient,
private configService: ConfigService,
) { }
}
ApiServiceReal
@Injectable()
export class ApiServiceReal implements ApiService {
constructor(
private http: HttpClient,
private configService: ConfigService,
) { }
}
Stackblitz示例:https://stackblitz.com/edit/angular-stackoverflow-41029733
的每个实现中声明的返回消息
更改useMockApi
中app.module.ts
属性的值,以测试在ApiService