Skip to content

Instantly share code, notes, and snippets.

@bennadel
Created January 23, 2019 13:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bennadel/f446ae4916a3064cebae7e02a2f8aaf8 to your computer and use it in GitHub Desktop.
Save bennadel/f446ae4916a3064cebae7e02a2f8aaf8 to your computer and use it in GitHub Desktop.
Providing Module Configuration Using forRoot() And Ahead-Of-Time Compiling In Angular 7.2.0
// Import the core angular services.
import { Component } from "@angular/core";
// Import the application components and services.
import { MyService } from "./my-service/my-service.module";
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
@Component({
selector: "my-app",
styleUrls: [ "./app.component.less" ],
template:
`
Testing forRoot() configuration, see console logging.
`
})
export class AppComponent {
// I initialize the app component.
constructor( myService: MyService ) {
console.group( "AppComponent Constructor" );
console.log( "myService injectable" );
console.log( myService );
console.groupEnd();
}
}
// Import the core angular services.
import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
// Import the application components and services.
import { AppComponent } from "./app.component";
import { MyServiceModule } from "./my-service/my-service.module";
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
@NgModule({
imports: [
BrowserModule,
// When importing the MyServiceModule, we can provide optional configuration
// data. This will be used, under the hood, to instantiate the MyServiceOptions
// and MySerivce classes.
MyServiceModule.forRoot({
retryInterval: 5000,
retryCount: 3
})
],
declarations: [
AppComponent
],
bootstrap: [
AppComponent
]
})
export class AppModule {
// ...
}
// Import the core angular services.
import { InjectionToken } from "@angular/core";
import { ModuleWithProviders } from "@angular/core";
import { NgModule } from "@angular/core";
// Import the application components and services.
import { MyService } from "./my-service.service";
import { MyServiceOptions } from "./my-service.service";
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
// Re-export services, treating the module like a "barrel".
export { MyService };
export { MyServiceOptions };
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
@NgModule()
export class MyServiceModule {
// I setup the module providers for the root application.
static forRoot( options?: ModuleOptions ) : ModuleWithProviders {
return({
ngModule: MyServiceModule,
providers: [
// In order to translate the raw, optional OPTIONS argument into an
// instance of the MyServiceOptions TYPE, we have to first provide it as
// an injectable so that we can inject it into our FACTORY FUNCTION.
{
provide: FOR_ROOT_OPTIONS_TOKEN,
useValue: options
},
// Once we've provided the OPTIONS as an injectable, we can use a FACTORY
// FUNCTION to then take that raw configuration object and use it to
// configure an instance of the MyServiceOptions TYPE (which will be
// implicitly consumed by the MyService constructor).
{
provide: MyServiceOptions,
useFactory: provideMyServiceOptions,
deps: [ FOR_ROOT_OPTIONS_TOKEN ]
}
// NOTE: We don't have to explicitly provide the MyService class here
// since it will automatically be picked-up using the "providedIn"
// Injectable() meta-data.
]
});
}
}
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
// I define the shape of the optional configuration data passed to the forRoot() method.
export interface ModuleOptions {
retryCount?: number;
retryInterval?: number;
}
// I am the token that makes the raw options available to the following factory function.
// --
// NOTE: This value has to be exported otherwise the AoT compiler won't see it.
export var FOR_ROOT_OPTIONS_TOKEN = new InjectionToken<ModuleOptions>( "forRoot() MyService configuration." );
// I translate the given raw OPTIONS into an instance of the MyServiceOptions TYPE. This
// will allows the MyService class to be instantiated and injected with a fully-formed
// configuration class instead of having to deal with the Inject() meta-data and a half-
// baked set of configuration options.
// --
// NOTE: This value has to be exported otherwise the AoT compiler won't see it.
export function provideMyServiceOptions( options?: ModuleOptions ) : MyServiceOptions {
var myServiceOptions = new MyServiceOptions();
// If the optional options were provided via the .forRoot() static method, then apply
// them to the MyServiceOptions Type provider.
if ( options ) {
if ( typeof( options.retryCount ) === "number" ) {
myServiceOptions.retryCount = options.retryCount;
}
if ( typeof( options.retryInterval ) === "number" ) {
myServiceOptions.retryInterval = options.retryInterval;
}
}
return( myServiceOptions );
}
// Import the core angular services.
import { Injectable } from "@angular/core";
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
@Injectable({
providedIn: "root"
})
export class MyServiceOptions {
public retryCount: number = 6;
public retryInterval: number = 2000;
}
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
@Injectable({
providedIn: "root"
})
export class MyService {
private options: MyServiceOptions;
// I initialize the my-service service.
constructor( options: MyServiceOptions ) {
this.options = options;
console.group( "MyService Constructor" );
console.log( "Injected Options" );
console.log( this.options );
console.groupEnd();
}
}
// CAUTION: THIS DOES NOT WORK WHEN USING AoT COMPILER.
// ....
static forRoot( options?: ModuleOptions ) : ModuleWithProviders {
return({
ngModule: MyServiceModule,
providers: [
{
provide: MyServiceOptions,
useFactory: () => {
var myServiceOptions = new MyServiceOptions();
myServiceOptions.retryCount = options.retryCount;
myServiceOptions.retryInterval = options.retryInterval;
return( myServiceOptions );
}
}
]
});
}
// ....
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment