Angular condition in type provider with AOT

醉酒当歌 提交于 2019-12-23 19:15:09

问题


I have an Angular project which I compile with AOT. I want to be able to register ClassProvider that is resolved dynamically according to configuration. Simplified code I use is this:

const isMock = Math.random() > 0.5;

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

The problem is when I compile this with AOT I always get the same service. I would expect to get different service while hitting F5 (because of the randomness on the first line). When compiling without AOT it behaves as I expect.

Here is the whole code example on github: https://github.com/vdolek/angular-test/tree/aot-conditioned-provider-problem. It behaves differently with ng serve and ng serve --aot.

How can I achieve this? I know I could use FactoryProvider, but then I would have to duplicate the services dependencies (parameters of the factory function and deps property on the FactoryProvider).


回答1:


To achieve the dynamic nature of your requirement, you need to use factory providers, via the useFactory attribute.

I've forked your repository, and amended your app.module.ts as follows, to work in AOT.

Amend app.module.ts as follows

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 {
}

In the case that your service is dependent on other services, which, most likely it will, you can use the deps argument, to pass on the required dependencies.

Let's say that MyServiceBase is dependent on two services, MyService1 and MyService2... Your factory function will look as follows :

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

  const isMock = Math.random() > 0.5;

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

and your providers decleration would look as follows

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

This guide contains further detail on the various ways of achieving dependency injection in Angular.




回答2:


I think as @jeanpaul-a said you don't have any choice other than to use factory. But managing dependencies could be not very clean. But what you could use is the Injector. I'll go with something like:

@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));
  }
}

What you could also do is to set MyServiceBase as an interface and use InjectionToken. You will find a working example here (not your class name however).



来源:https://stackoverflow.com/questions/48908735/angular-condition-in-type-provider-with-aot

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!