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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With