最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Angular condition in type provider with AOT - Stack Overflow

programmeradmin0浏览0评论

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: . 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).

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).

Share Improve this question edited Feb 22, 2018 at 10:01 Martin Volek asked Feb 21, 2018 at 14:44 Martin VolekMartin Volek 1,0994 gold badges13 silver badges29 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 14

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.

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).

发布评论

评论列表(0)

  1. 暂无评论