We want to provide a GreetingService
that can be configured and initialized before it is injected to any other component, directive, or pipe.
We also want the service provided as separate instances to our application module and any lazy loaded modules.
This is the final demo.
Approach
First create a new Angular Stackblitz and create the folder src/app/services
.
Greeting Service Configuration
Create src/app/services/greeting-service.config.ts
.
export class GreetingServiceConfig {
public greeting: string = 'Hello';
public subject: string = 'Angular';
}
Injection Token
Create src/app/services/greeting-service.token.ts
.
import { InjectionToken } from '@angular/core';
import { GreetingServiceConfig } from './greeting-service.config';
export const GREETING_SERVICE_CONFIG_INJECTION_TOKEN =
new InjectionToken<GreetingServiceConfig>(
'greeting-service_config_injection_token'
);
The InjectionToken
instance will be used to inject a GreetingServiceConfig
instance into our GreetingService
instances.
Greeting Service
Create src/app/services/greeting.service.ts
.
import { Inject, Injectable } from '@angular/core';
import { GreetingServiceConfig } from './greeting-service.config';
import { GREETING_SERVICE_CONFIG_INJECTION_TOKEN } from './greeting-service.token';
@Injectable()
export class GreetingService {
constructor(
@Inject(GREETING_SERVICE_CONFIG_INJECTION_TOKEN)
private config: GreetingServiceConfig
) {}
public initialize() {
this.salutation = `${this.config.greeting} ${this.config.subject}!`;
}
public greeting(message: string): string {
return `${this.config.greeting} ${message}!`;
}
public salutation: string;
}
Note that the constructor injects the GreetingServiceConfig
via the InjectionToken
.
@Inject(GREETING_SERVICE_CONFIG_INJECTION_TOKEN) private config: GreetingServiceConfig
Greeting Service Module
Create src/app/services/greeting-service.module.ts
.
import { ModuleWithProviders, NgModule } from '@angular/core';
import { GreetingService } from './greeting.service';
import { GreetingServiceConfig } from './greeting-service.config';
import { GREETING_SERVICE_CONFIG_INJECTION_TOKEN } from './greeting-service.token';
@NgModule({
providers: [GreetingService],
})
export class GreetingServiceModule {
constructor(greetingService: GreetingService) {
greetingService.initialize();
}
static config(
config?: Partial<GreetingServiceConfig>
): ModuleWithProviders<GreetingServiceModule> {
return {
ngModule: GreetingServiceModule,
providers: [
{
provide: GREETING_SERVICE_CONFIG_INJECTION_TOKEN,
useValue: Object.assign(new GreetingServiceConfig(), config),
},
],
};
}
}
We have created a config(config?: Partial<GreetingServiceConfig>)
method with a return type:
ModuleWithProviders<GreetingServiceModule>
We made the config?
argument a Partial
allowing us to omit configuration parameters that we want to leave as default.
The returned module provides the GreetingServiceConfig
instance that will be injected into the GreetingService
instance provided by the module.
In addition we also in inject the GreetingService
via the GreetingServiceModule
constructor and call initialize on the instance, such that the service becomes initialized prior to being injected into clients.
export class GreetingServiceModule {
constructor(greetingService: GreetingService) {
greetingService.initialize();
}
...
Application Module
Now that we have completed the GreetingServiceModule
we can import and declare a configuration for the GreetingService
within the app-module.ts
.
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { HelloComponent } from './hello.component';
import { GreetingServiceModule } from './services/greeting-service.module';
import { AppRoutingModule } from './app-routing.module';
@NgModule({
imports: [
BrowserModule,
FormsModule,
AppRoutingModule,
GreetingServiceModule.config({ greeting: 'Hola' }),
],
declarations: [AppComponent, HelloComponent],
bootstrap: [AppComponent],
})
export class AppModule {}
In the above case we are setting the greeting
property to Hola
.
GreetingServiceModule.config({ greeting: 'Hola' }),
We inject the GreetingService
into the salutation we get Hola Angular!
.
constructor(g: GreetingService) {
console.log(g.salutation);
}
This indicates to us that the service was initialized in the GreetingModule
constructor prior to its injection into the AppComponent
.
Lazy Module
Our demo also contains a lazy loaded module that can be loaded by clicking the Load Lazy
button in the AppComponent
.
<button routerLink="/lazy">Load Lazy</button>
If we do this we see that when loaded it logs.
Lazy Loaded and the greeting is Ciao Bella!
This indicates that the LazyModule
has its own GreetingService
instance configured with the greeting
property set to Ciao
.
@NgModule({
declarations: [LazyComponent],
imports: [
CommonModule,
LazyRoutingModule,
GreetingServiceModule.config({
greeting: 'Ciao',
}),
],
})
export class LazyModule {}