0

Context

In an Angular 9 project, I am working with two environments: production & mock.

In the Core Module, I check for mock environment.

  • If build is made with mock configuration I inject mocked services that return mocked data, so no external http requests are made.

  • If build is made with prod configuration, real services are injected.

I do it like this:

 core.module.ts

@NgModule({
  declarations: [],
  providers: [],
  imports: [BrowserModule, HttpClientModule],
  exports: [],
})
export class CoreModule {}

country.service.proxy.ts

const countryServiceFactory = (
  _http: HttpClient,
  _errorUtil: ErrorUtilService
) => {
  return isMock
    ? new ServiceMock()
    : new Service(_http, _errorUtil);
};

@Injectable({
  providedIn: CoreModule,
  useFactory: countryServiceFactory,
})
export abstract class CountryServiceProxy {
  abstract getCountries(): Observable<CountryWithLanguages[]>;
}

Where ServiceMock and Service implement the same interface.

This works.

Problem

Code is not tree shakeable. The result is that in my bundle (when I run ng build --prod) even the mock services are included.

I want to switch each service from mock to prod during development.

Goal

How can I make Angular to bundle only the service that it is going to be used?


I am using:

Angular CLI: 9.0.4
Node: 13.6.0
OS: darwin x64

Ivy Workspace: Yes

Thank you! :)

Community
  • 1
  • 1
adrisons
  • 3,443
  • 3
  • 32
  • 48
  • Have you tried setting the different versions in your `environment.ts` files? I don't actually know if this would work or not - just a suggestion! – Kurt Hamilton Mar 06 '20 at 12:06
  • @KurtHamilton versions of what? My environment has the keys: `mock` and `production` – adrisons Mar 06 '20 at 12:50

1 Answers1

2

I have just tried one approach that seems to work:

  • Declare the relevant service factory in your environment.{env}.ts files
  • Use the environment factory as your service provider

My test setup:

base class

@Injectable()
export abstract class TestService {
  abstract environment: string;
}

dev service

@Injectable()
export class DevTestService extends TestService {
  environment = 'qwertydev';
}

prod service

@Injectable()
export class ProdTestService extends TestService {
  environment = 'qwertyprod';
}

environment.ts

export const environment = {
  testServiceFactory: () => new DevTestService()
};

environment.production.ts

export const environment = {
  testServiceFactory: () => new ProdTestService()
};

app.module.ts

providers: [
  { provide: TestService, useFactory: environment.testServiceFactory }
],

app.component.ts

constructor(private testService: TestService) {}

ngOnInit() {
  console.log(this.testService.get());
}

When I inspect my build files, I only find qwertydev in the dev build, and qwertprod in the prod build, which suggests that they have been tree-shaken.

I used the strings qwerty* to make it easy to search the build files after minification.

Declaring services in the module

I have declared the provider in the module to avoid circular references. It is easy to introduce a circular reference by declaring a service as providedIn: Module.

You can work around this by declaring a third-party module, but this seems overkill.

I have demonstrated this in an older answer: @Injectable() decorator and providers array

Alternative approaches

It doesn't quite feel right declaring service factories in the environment files. I did it for testing just for simplicity. You could create you own set of environment-specific files that are overwritten at build time in the same way as the environment files, but quite frankly this sounds like a maintenance nightmare.

Kurt Hamilton
  • 12,490
  • 1
  • 24
  • 40
  • Thank you! It worked :) Mock services do not appear in the bundle. One weird thing that is happening is that the bundle size is the same although those files are not included. Do you know why? – adrisons Mar 09 '20 at 07:38
  • 1
    Weird. I've been having some unexpected results with bundle sizes recently. I'm not yet aware of any obvious reasons why this might be, although I've not really done a huge amount of investigation. – Kurt Hamilton Mar 09 '20 at 07:40