0

I have a pretty simple design with my AppModule definition. I had a set of Mocked Services in my providers, which leveraged local data until my endpoints can get resolved. I was slowly migrating them over as the service endpoints are stood up. The issue is that the application is very requires some config to get the endpoints up and running, so i created a simple config file which created a property: useMockedServices for people who want to use a demo.

{
  useMockedServices: true
}

BUT, my question is. How do i take this configuration into account for the providers?

...
providers: [
  {provide: UserService, useClass: MockUserService},
  ...
],
...

Could I do something like:

providers: [
  { provide: MAT_DIALOG_DEFAULT_OPTIONS, useValue: { hasBackdrop: false }},
  ...
].concat( config.useMockedServices ? [
  {provide: UserSerice, useValue: MockUserService},
  ...
]: [],

I tried to do just that but I am getting an error: No Overload matches this call and it says "The Types of property slice are incompatible"

providers: [
    // Default Options for using Modals / Dialogs.
    { provide: MAT_DIALOG_DEFAULT_OPTIONS, useValue: { hasBackdrop: false } },
    { provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
    { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
    { provide: AuthGuard, useClass: AuthGuard },
    { provide: AuthenticationService, useClass: AuthenticationService }
  ].concat(
    configuration.useMockServices === true ? [
    { provide: UserService, useClass: MockUserService },
    ...
    ] : []
  ),
...
Fallenreaper
  • 10,222
  • 12
  • 66
  • 129
  • Maybe this can help? https://stackoverflow.com/questions/48908735/angular-condition-in-type-provider-with-aot – yazantahhan Oct 11 '19 at 21:44
  • @yazantahhan That answer's implementation might work, but it isnt sustainable. I have 15 services, each of which would require a function of their own. I would figure that there is a better approach. If it was a 1-off service, i would consider it as an answer, but given bulk requirements I wouldnt. – Fallenreaper Oct 14 '19 at 13:07

1 Answers1

1

You can use useFactory to do that:

function userServiceFactory() {
   return enviroment.useMockedServices ? UserServiceMock : UserService;
}


...

providers: [
  {provide: UserService, useFactory: userServiceFactory},
  ...
]

or if you have the config into service.

function userServiceFactory(config: ConfigService) {
   return config.useMockedServices ? UserServiceMock : UserService;
}


...

providers: [
  {provide: UserService, useFactory: userServiceFactory, deps:[ConfigService]},
  ...
]
Serginho
  • 7,291
  • 2
  • 27
  • 52
  • As discussing in the comments, the requirement is to then create a factory for EACH function, which is not ideal. I dont like that, it is an answer but it isnt really a generic solution im looking for as I was aiming to just use Defaults if none was supplied. – Fallenreaper Oct 14 '19 at 13:21
  • There is no generic solution developed from now, dependency injection is static and imports are required by webpack to connect javascript modules. You lose more than you win. You can use a generic factory and add `window[serviceName + 'Mock']` as string, but you lint will throw an error that you have unused imports that you really are importing and probably tree-shaking fails. – Serginho Oct 14 '19 at 15:40
  • I didnt want to add explicit definitions to do: `{provide: UserService, useClass:UserService}` as that just seems needless. I can easily do something like this though: `{provide: UserService, useClass: config.useMockedServices === true ? MockUserService: UserService }`, I was just hoping that there was a way to *not* include the provide if i wasnt actively changing it, as it just assigns *self* to *self* in my sample here. – Fallenreaper Oct 14 '19 at 20:01