1

I would like to inject an implementation of an interface into an Ionic2 component (Ionic2 Beta 10, uses Angular2 RC4). Lets say we have the following service:

export interface ServiceInterface { /*...*/ }
@Injectable()
export class ServiceImpl implements ServiceInterface { /*...*/ }

How do I inject that into an Ionic2 component? I have tried:

import { Component, provide, Provider } from '@angular/core';
import { ServiceInterface } from './service.interface';
import { ServiceImpl } from './service.impl';

@Component({
    providers: [
        // Exception: Can't resolve all parameters for SomeComponent: (?)
        ServiceImpl,

        // @deprecated - seen on: http://blog.thoughtram.io/angular/2015/05/18/dependency-injection-in-angular-2.html
        provide(ServiceInterface, {useClass: ServiceImpl}),

        // @deprecated - seen on: http://plnkr.co/edit/RSTG86qgmoxCyj9SWPwY?p=preview
        new Binding(ServiceInterface, {toAlias: ServiceImpl}),

        // @deprecated - seen on: https://www.dartdocs.org/documentation/angular2/2.0.0-beta.9/angular2/Provider/useClass.html
        new Provider(ServiceInterface, {useClass: ServiceImpl}),

        // TS Error: Cannot find name 'ServiceInterface' - even though it is properly referenced
        {provide: ServiceInterface, useClass: ServiceImpl}
    ]
})
export class SomeComponent {
    constructor(private service: ServiceInterface) {}
}

Simply using the ServiceImpl as follows works perfectly fine, but that is not what I want:

@Component({providers: [ServiceImpl]})
export class SomeComponent {
    constructor(private service: ServiceImpl) {}
}

Any ideas what I am missing?

Fabian Kleiser
  • 2,988
  • 3
  • 27
  • 44
  • Possible duplicate of [Binding a class to an interface](http://stackoverflow.com/questions/32254952/binding-a-class-to-an-interface) – sebaferreras Aug 01 '16 at 15:40

1 Answers1

1

In TypeScript, interfaces are not retained at runtime (they are simply gone), thus the Cannot find name 'ServiceInterface' error.

When you use

{provide: ServiceInterface, useClass: ServiceImpl}

The provide part is a token. You are using a non-existing token now (the interface). A possible workaround would be to use a string as token instead.

Another possible way is to use an OpaqueToken instead of a string. You could declare one with the same name as the interface. Here's how it could be done:

import { Component, Injectable, provide, Provider, OpaqueToken, Inject } from '@angular/core';

export interface ServiceInterface { /*...*/ }
@Injectable()
export class ServiceImpl implements ServiceInterface { /*...*/ }

const ServiceInterface = new OpaqueToken("ServiceInterface");  // <----- token defined

@Component({
  selector: 'my-app',
  template: '<h1>My First Angular 2 App</h1>',
  providers: [{provide: ServiceInterface, useClass: ServiceImpl}] // <-- providing
})
export class AppComponent {
    constructor(@Inject(ServiceInterface) private service: ServiceInterface) {}
                        ^^^^^^^^^^^^^^^^-- injecting
}

See demo plunker.

acdcjunior
  • 132,397
  • 37
  • 331
  • 304
  • PS.: This `OpaqueToken` technique is largely used at the angular source itself: https://github.com/angular/angular/search?utf8=%E2%9C%93&q=OpaqueToken – acdcjunior Aug 01 '16 at 15:43
  • That is unexpected, there is definitely room to improve the developer experience! The `OpaqueToken` still leaves a lot room for confusion and careless mistakes. – Fabian Kleiser Aug 03 '16 at 07:06