‹‹ All posts

Injectable environment configuration in Angular 4

08 of June, 2017


Injectable Service

Dependency Injection in Angular 4 (typescript) is frictionless and requires only minimal amount of boilerplate:

  1. Define an object injectable;
  2. Add it to list of providers;
  3. Everything else is happening behinds the scenes.

This has tons of examples on the net, and here’s one more:

// src/app/app.module.ts
import {SampleInjectable} from './sampleInjectable'
@NgModule({
    providers: [SampleInjectable]
})

// src/app/sampleInjectable.ts
@Injectable
export class SampleInjectable {
  name: string = 'burokas';
}

SampleInjectable now can be used in other services or components (just type hint it in constructor and Angular will do the rest).

Injectable service, using environment configuration

The name is hardcoded though. Would be much better to make it configurable. Imagine it is dependent on environment.

Angular 4 provides all the infrastructure for defining such parameters, no reinvention of wheel is necessary:

// src/app/app.module.ts
import {SampleInjectable} from './sampleInjectable'
@NgModule({
    providers: [SampleInjectable]
})

// environment/environment.ts
export const environment = {
    production: false,
    sampleName: 'burokas222'
}

// src/app/sampleInjectable.ts
import {environment} from '../environment/environment';
@Injectable
export class SampleInjectable {
  name: string = environment.sampleName;
}

This is much better. The hard dependency on environment is still a big flaw, would be much better to inject the string, instead of coupling object to app configuration. But how?

Injectable service, using injected environment configuration

Injectable parameters (scalar values) in Angular 4 are less magic then injectable objects and requires more code, but just a little bit. Here’s what needs to be done:

  1. Define custom token;
  2. Define provider mapping this token to scala value;
  3. Add it to list of app providers;
  4. Inject name via constructor dependency injection.

Here’s example:

// environment/environment.ts
export const environment = {
    production: false,
    sampleName: 'burokas222'
}

// src/app/config.ts
import {InjectionToken} from '@angular/core';
import {environment} from '../environment/environment';
export const SampleName = new InjectionToken<string>('sample-name');
export const SampleNameEnv = { provide: SampleName, useValue: environment.sampleName };

// src/app/app.module.ts
import {SampleInjectable} from './sampleInjectable'
import {SampleNameEnv} from './config'
@NgModule({
    providers: [SampleNameEnv, SampleInjectable]
})

// src/app/sampleInjectable.ts
import {SampleName} from './config';
@Injectable
export class SampleInjectable {
  constructor(@Inject(SampleName) name: string);
}

Is it worth it?

Absolutely:

  1. The service is not tightly coupled on configuration anymore;
  2. Now can be unit tested easily and used outside of the app too;
  3. The amount of boilerplate code is minimal;
  4. You can define different name in environments/environment.prod.ts, which may not make much sense in this example, but would be very useful when service in question is an api client with injected base url.

Alternative: value object with configuration

When there are many parameters in environment configuration, then making each injectable could quickly become quite repetitive. In that case it could be grouped into configuration value object and passed around like together instead.

comments powered by Disqus