Angular版本:4.4.6
编辑:这是一个很复杂的例子,可能对您不感兴趣。
TL; DR:我的服务无法动态注入,因为它没有被任何模块提供(我对通过InjectionToken传递服务引用感到困惑,哪些不提供服务。)
我的 ResourceService 服务被设计为代理到 ResourceProviders 服务,其列表可以通过 ResourceModule .forRoot方法。
资源provider.ts
import { Injectable } from '@angular/core';
@Injectable()
export class ResourceProvider {
// whatever...
}
resource.module.ts
import { NgModule, ModuleWithProviders } from '@angular/core';
import { SourceResourceService, SOURCE_TOKEN } from './source-resource.service';
@NgModule({
})
export class ResourceModule {
/**
* forRoot awaits a list of [[ResourceProvider]]s to be used by the
* various [[ResourceService]]s.
*/
static forRoot( sourceProviders: any[] ):
ModuleWithProviders {
return {
ngModule: ResourceModule,
providers: [
{ provide: SOURCE_TOKEN, useValue: sourceProviders },
SourceResourceService
]
}
}
}
资源service.ts
import { Injector, InjectionToken } from '@angular/core';
import { ResourceProvider } from './resource-provider';
/**
* Base class for ResourceServices
*/
export abstract class ResourceService {
injector: Injector;
resourceProviders = new Array<ResourceProvider>();
constructor( injector: Injector ) {
// Injector is used to inject [[ResourceProvider]]s in the list 'resourceProviders',
// during the initialization of the Service.
this.injector = injector;
}
protected initResourceProviders(list: any[]) {
let providerClass: any; // what type should that be in Typescript?
for( providerClass of list ) {
let service;
try {
//!\ BREAKS HERE /!\
service = this.injector.get(providerClass);
//!\ BREAKS HERE /!\
if(!service) {
console.error( "ResourceService: " + providerClass + " is not a registered service");
} else {
this.resourceProviders.push( service );
}
}
catch( err ) {
console.error( "ResourceService: " + providerClass + " is not a registered service", err);
}
}
}
//...
}
源resource.service.ts
import { Injectable, Injector, Inject, InjectionToken } from '@angular/core';
import { ResourceService } from './resource-service';
export const SOURCE_TOKEN = new InjectionToken('SOURCE_RESOURCE_PROVIDERS_TOKEN');
@Injectable()
export class SourceResourceService extends ResourceService {
constructor(
private injectorParam: Injector,
@Inject(SOURCE_TOKEN) SOURCE: any[]
){
super( injectorParam );
this.initResourceProviders(SOURCE);
}
//...
}
source-resource.service.spec.ts (使用Karma测试)
import { async, TestBed } from '@angular/core/testing';
import { Injectable } from '@angular/core';
import { ResourceModule } from './resource.module';
import { SourceResourceService } from './source-resource.service';
import { ResourceProvider } from './resource-provider';
@Injectable()
class p0 extends ResourceProvider {
//...
}
/**
* Configure the ResourceProviders in the ResourceModule for the tests.
*/
function setTestResourceProviders(source: any[]): SourceResourceService {
TestBed.configureTestingModule({
imports:[
ResourceModule.forRoot(source)
]
});
return TestBed.get(SourceResourceService);
}
describe('ResourceService', () => {
let resourceService: SourceResourceService;
it("should proceed without throwing exception", ()=>{
resourceService = setTestResourceProviders([p0]);
//...
});
});
尽管我在ResourceModule定义的useValue中传递了ResourceProvider列表(包含p0),但是this.injector.get(p0)会抛出异常错误:p0没有提供者!(在控制台中可以看到)我的resourceService无法进一步使用和测试。
为什么通过&#39; useValue&#39;注入ResourceProvider?在这里失败了?
奖金问题:我提供给“使用价值”的资源提供者列表的Typescript类型是什么?
答案 0 :(得分:0)
答案发现:在ModuleMithProviders的ResourceModule.forRoot()创建中存在混淆:我提供了一些包含我的服务引用列表的令牌,但我没有&# 39; t 提供这些服务。因此,注射器无法找到那些。
resource.module.ts 的更正代码为:
static forRoot( sourceProviders: any[]):
ModuleWithProviders {
return {
ngModule: ResourceModule,
providers: [
{
provide: SOURCE_TOKEN,
useValue: sourceProviders
}
SourceResourceService,
...sourceProviders,
]
};
}
尝试生成&#39;提供商&#39;返回语句之前的数组,AoT构建失败并出现错误:在静态解析符号值时遇到错误。调用函数&#39; ResourceModule&#39;,不支持函数调用。,所以看起来这个过程有些敏感。我通过在“提供商”中使用扩展运算符(... sourceProviders)解决了这个问题。数组直接。