Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2 Injectable Interface?

Today I stumbled upon something that I didn't think would cause me trouble.

In Java and Spring, I can declare two beans that both implement a given interface, while in another class where they are injected I only work with the interface; this is in fact what I love with IoC: you don't really have to know what object you're working with, only it's kind.

So in my little Angular2/Typescript program, I was trying to do the same:

webapp.module.ts:

... 
import { WebAppConfigurationService } from './app/services/webapp.configuration.service';

@NgModule({
  ...
  providers: [WebAppConfigurationService]
})
export class AppModule { }

tnsapp.module.ts:

...
import { TnsConfigurationService } from './services/tns.configuration.service';

@NgModule({
   ...
   providers: [TnsConfigurationService]
})
export class AppModule { }

Both of these modules are using a different provider: TnsConfigurationService or WebAppConfigurationService.

However, these two @Injectable services implement the same interface:

configuration.interface:

export interface IConfigurationService {
    ...
}

Finally, in one of my components, I use the injectable provided by one of these modules I showed you at the beginning:

import { IConfigurationService } from './configuration.interface';

export class HeroesService {

    constructor(private configurationService: IConfigurationService) { }
}

My expectation was that this last component being injected with the right service, even though the parameter is only explicitely defining the interface. Of course I get an error ("Error: Can't resolve all parameters for HeroesService")

Now, I don't expect an easy solution for this as it sounds as an architectural lack. But maybe someone can point me out to an alternative design?

like image 560
Sebas Avatar asked Feb 23 '17 17:02

Sebas


1 Answers

In order for a provider to be injected, it should be registered as a provider. There's no IConfigurationService provider. And it cannot be a provider, because interfaces don't exist in compiled JS code.

The common practice for interfaces that are supposed to be used as provider tokens is to be abstract classes:

abstract class ConfigurationService { ... }

@Injectable()
class WebAppConfigurationService extends ConfigurationService { ... }

...
providers: [{ provide: ConfigurationService, useClass: WebAppConfigurationService }]
...

This recipe is commonly used by Angular 2 itself, e.g. abstract NgLocalization class and concrete NgLocaleLocalization implementation.

like image 180
Estus Flask Avatar answered Sep 19 '22 04:09

Estus Flask