AOT下使用类型提供程序的Angular条件

7
我有一个使用AOT编译的Angular项目。我希望能够根据配置动态地注册ClassProvider。我使用的简化代码如下:
const isMock = Math.random() > 0.5;

@NgModule({
  // ...
  providers: [
    { provide: MyServiceBase, useClass: (isMock) ? MyServiceMock : MyService },
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

当我使用AOT编译时,问题在于我总是得到相同的服务。我期望在按F5时获得不同的服务(因为第一行有随机性)。当没有使用AOT编译时,它的行为符合我的期望。
这里是github上完整的代码示例: https://github.com/vdolek/angular-test/tree/aot-conditioned-provider-problem。它在使用ng serveng serve --aot时表现不同。
我该如何实现这一点?我知道我可以使用FactoryProvider,但那样我就必须复制服务依赖项(工厂函数的参数和FactoryProvider中的deps属性)。
2个回答

12
为了满足您的动态需求,您需要使用工厂提供者,通过useFactory属性。
我已经克隆了你的存储库,并修改了你的app.module.ts如下,以在AOT中工作。
如下修改app.module.ts
export let myServiceFactory = () => {

  const isMock = Math.random() > 0.5;

  return isMock ? new MyServiceMock() : new MyService();
}; 

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [
    {provide: MyServiceBase, useFactory: myServiceFactory},
  ],
  bootstrap: [AppComponent]
})
export class AppModule {
}

如果您的服务依赖于其他服务(很可能是这样),则可以使用deps参数传递所需的依赖项。

假设MyServiceBase依赖于两个服务:MyService1MyService2... 您的工厂函数将如下所示:

export let myServiceFactory = (service1:MyService1, service2:MyService2) => {

  const isMock = Math.random() > 0.5;

  return isMock ? new MyServiceMock(service1, service2) : new MyService(service1, service2);
}; 

你的提供者声明将如下所示

providers: [
    {
       provide: MyServiceBase, 
       useFactory: myServiceFactory,
       deps: [MyService1, MyService2]
    },
]

本指南详细介绍了在Angular中实现依赖注入的各种方法。


感谢您的回答。正如我在问题中提到的,我熟悉FactoryProviders。但是我不喜欢需要三次列出依赖项(在实际服务中,在工厂签名中以及在“deps”属性中)。我想避免这种代码重复-这就是问题的目的。deps属性甚至在构建时都没有静态检查,因此当有人更改服务的依赖项时,他必须知道更改deps属性。 - Martin Volek
我明白了,我必须承认,我错过了那句话(我的思维自动转向了那个解决方案)。据我所知,没有其他方法可以实现您所需的内容。 - JeanPaul A.
Angular 5在使用AOT时减少了对工厂函数等语法的限制。 - Matt Strom

2

我认为像@jeanpaul-a所说的那样,你没有选择,只能使用工厂。但是管理依赖关系可能不太干净。但是你可以使用的是Injector。我会用类似这样的方式:

@NgModule({
  imports:      [ BrowserModule, FormsModule ],
  declarations: [ AppComponent, HelloComponent ],
  providers: [
    Dep1Service,
    Dep2Service,
    { provide: MyServiceBase, useFactory: createService, deps: [Injector] }
  ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

export function createService(injector: Injector) {
  const isMock = Math.random() > 0.5;
  if (mock) {
    return new MyService1(injector.get(Dep2Service));
  } else {
    return new MyService2(injector.get(Dep1Service));
  }
}

您还可以将MyServiceBase设置为接口,并使用InjectionToken

您可以在此处找到一个可行的示例(但不是您的类名)。


1
感谢您的回答。这并不完全符合我的期望,但至少每当依赖关系发生变化时,它会产生构建错误,因此人们知道需要更新工厂。 - Martin Volek
是的,我知道,但您无法在使用useClass时调用函数,而且如果使用AOT编译,您的表达式将是静态的。因此,如果您想要一些动态性,您需要一个工厂。 - JEY

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接