Angular:通过InjectionToken注入服务失败

时间:2018-01-31 16:31:01

标签: angular dependency-injection

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类型是什么?

1 个答案:

答案 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)解决了这个问题。数组直接。