Angular 2 Interface Dependency Injection

December 2, 2016    JavaScript Development Angular2

Angular 2 Interface Dependency Injection (not Angular 5+)

I want to be able to switch between my EnergyDataJSONService, EnergyDataService (which will call the real HTTP api), or a test data service in the app.module.ts or in a spec. The Angular 2 guide on Dependency Injection shows that we need to use an OpaqueToken for interface based injection.

NOTE: 11/15/2017 the link to the OpaqueToken is broken and is now in the archives. The class-interface is now Angular’s suggestion on how to switch between instances.

The code and text below was written for Angular v2. I strongly suggest you update to the latest version and use the prescribe approach in their documentation.


It says “TypeScript interfaces aren’t valid tokens… that seems strange if we’re used to dependency injection in strongly typed languages, where an interface is the preferred dependency lookup key. It’s not Angular’s fault. An interface is a TypeScript design-time artifact. JavaScript doesn’t have interfaces. The TypeScript interface disappears from the generated JavaScript. There is no interface type information left for Angular to find at runtime.”

I wish Angular could handle this for me, but I understand there are difficulties.

The Angular 2 (not 5+) guide only shows an example that exports a const. That’s great, but I need to use a real class.

export interface AppConfig {
    apiEndpoint: string;
    title: string;
}

export const HERO_DI_CONFIG: AppConfig = {
    apiEndpoint: 'api.heroes.com',
    title: 'Dependency Injection'
}

Here is my solution. Thank you to Günter Zöchbauer and his answer on StackOverflow. Also see the Angular 2 cookbook.

My example is the IEnergyDataService that the EnergyDataService and EnergyDataJsonService. See the energy/energy-data-service.token.ts.

import { OpaqueToken } from '@angular/core';
export let IEnergyDataServiceToken = new OpaqueToken('./energy-data-service.interface');`

app.module.ts has

import EnergyDataJsonService from './energy/energy-data-json.service';
import {IEnergyDataServiceToken} from './energy/energy-data-service.token';
....
providers: [{ provide: IEnergyDataServiceToken, useClass: EnergyDataJsonService }]`

and energy/energy.component.ts gets the token injected

import {IEnergyDataService} from './energy-data-service.interface';
import {IEnergyDataServiceToken} from './energy-data-service.token';
...
constructor(@Inject(IEnergyDataServiceToken) private energyDataService: IEnergyDataService, private router: Router) {}

Please note the component’s usage of @Inject, the energy-data-service.token.ts for the OpaqueToken usage, and the providers array in app.module.ts.

I’m using "@angular/common": "2.2.1"



comments powered by Disqus

Please consider using Brave and adding me to your payment ledger. Then you won't have to see ads!

Support me and download Brave!

Use Brave