[angular] Inject different Service into a Service.

Photo by Markus Winkler on Unsplash

Dependency Injection is important concept of angular structure, instead of creating new instance manually, you can just declare it on the module or component.

We can use same instance for the whole app, but some times you might encounter the situation that requires different instance to each component.

But is it possible to inject different type of instance into another service?

Let’s say we have ScoreService says good|great|notBadand we have 2 different components which show them in different languages.

class ScoreService(private scoreI18nService: ScoreI18nService){  name: string;
good(){
return this.name + ', ' + this.scoreI18nService.good();
}
great(){
return this.name + ', ' + this.scoreI18nService.great();
}
notBad(){
return this.name + ', ' + this.scoreI18nService.great();
}
}

EnglishScoreComponent needs it in English, and KoreanScoreComponent in Korean except that, everythings in the tree are the same. You can add argument to every method to check which language service should answer, but if you have deep long path down, it can be bothersome to check everywhere you use it, or you can put different ScoreI18nService in ScoreService on the spot and adopt it.

Inject Service into Service

First of all, you need to wrap them with a component and provide all the services to be used under component level. This is where the provider gets different.

@Component({
selector: 'app-score-in-english',
providers: [
ScoreService,
ScoreI18nService,
], ...
})
export class EnglishScoreComponent {}

Make Interface with Abstract Class

Here, the instance in charge of language is ScoreI18nService and we need to switch this class based on the language, so let’s make abstract class to inherit. It should have good, great, notBad to be used in ScoreService, so abstract class should have 3 of them.

abstract class ScoreI18nService {  abstract good(): string
abstract great(): string
abstract notBad(): string
}

Inject different service

Now we make englishService extends ScoreI18nService and replace providing ScoreI18nService part with the following code.

const ScoreI18nKey = new InjectionToken('any can come here, but better use InjectionToken');class ScoreEnglishService extends ScoreI18nService {
good(){return 'good'}
great(){return 'great'}
notBad(){return 'notBad'}
}
@Component({
selector: 'app-score-in-english',
providers: [
ScoreService,
{
provide: ScoreI18nKey,
useClass: ScoreEnglishService
}
],
...
})
export class EnglishScoreComponent {}

The value you put at provide is identifier, and here you can use various strategy. (check more info of provider and injection token here)

ScoreService should receive a class inheriting ScoreI18nService class, but ScoreService does not know which class will be injected, so we receive it with identifier decorated by Inject decorator.

class ScoreService {
constructor(
@Inject(ScoreI18nKey) private scoreI18nService: ScoreI18nService){}
}

Now we declare KoreanScoreComponent with the same way.
Here’s the whole code.

I hope it is helpful :)

Thank you for reading.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store