12

Is there a way to conditionally change imports based on an environment variable in angular-cli@1.0.0-beta.16? I'm trying to do it in a way that doesn't require code changes in how services are imported in client code, but when needed I can specify a build flag to swap in mock services.

There is a pattern I tried using from this post:

File structure:

MyService
    MyServiceMock.ts
    MyServiceReal.ts
    index.ts

And in your index.ts, you can have the following:

import { environment} from '../environments/environment';

export const MyService = environment.mock ?
    require('./MyServiceMock').MyServiceMock:
    require('./MyServiceReal').MyServiceReal;

And in your client code, import MyService:

import MyService from './myservice/index';

The page loads, and I can see the dependency getting injected when stepping through the code, however there are compilation errors (which I believe are TypeScript errors) along the lines of Cannot find name 'MyService'.

Community
  • 1
  • 1
Daren Keck
  • 123
  • 1
  • 1
  • 6

2 Answers2

14

You're going about it completely wrong. Angular can handle this use case with the use of factories when you configure the providers

providers: [
  Any,
  Dependencies
  {
    provide: MyService, 
    useFactory: (any: Any, dependencies: Dependencies) => {
      if (environment.production) {
        return new MyService(any, dependencies);
      } else {
        return new MockMyService(any, dependencies);
      }
    },
    deps: [ Any, Dependencies ]
]

Now you can just inject MyService everywhere because of the provide: MyService, but in development, you will get the mock, and in production you will get the real service.

See Also:

monty
  • 1,543
  • 14
  • 30
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • 7
    This approach works well, but has a drawback, that mock services are included into production package – ENargit Apr 16 '17 at 20:04
  • How exactly do I inject the provider when it's created this way? Normally I would import from the file the service lives in, but in this case it could one of two files. How do I tell Angular I want the provider from the provided factory function? – Valevalorin Aug 04 '17 at 18:15
  • @Valevalorin by the token (the `provide`), which is `MyService`. That's what you inject. – Paul Samsotha Aug 04 '17 at 23:31
  • I need to know specifically what the line beginning with `import` looks like. – Valevalorin Aug 07 '17 at 15:40
  • @Enargit list the mock and the real service in a module and then conditionally load that module in the imports section. Also look at, where I got this answer from: https://stackoverflow.com/a/51772549/2733437 – David Zwart Dec 30 '19 at 14:28
0

Change your MyService import to:

import { MyService } from './myservice/index';

The surrounding {} will tell the compiler to import a single export from the file. If you want to be able to import like:

import MyService from './myservice/index';

Then you must have a default export in index.ts e.g:

export default MyService; .

More information on TypeScript modules can be found here: https://www.typescriptlang.org/docs/handbook/modules.html

shusson
  • 5,542
  • 34
  • 38
  • Thank you for the idea. I am still running into the same error though, with `Cannot find name 'MyService'`. Also potentially of note: the static analysis of the typescript in VS Code echos the same error, indicating that the import is resolving, but when attempting to do dependency injection in the constructor it is not resolving the name. – Daren Keck Oct 13 '16 at 23:42