I have a service in sub-module that wraps some third-party module, instantiates and initializes its service to prepare for use within app.
@Injectable()
class SubmoduleInitializerService {
constructor (thirdPartyService: ThirdPartyService) {
thirdPartyService.initialize(...);
...
}
}
@NgModule({
imports: [ThirdPartyModule],
exports: [ThirdPartyModule],
providers: [
ThirdPartyService,
SubmoduleInitializerService
]
})
class AppSubmodule {}
ThirdPartyService
isn't injected in app directly but is used by other ThirdPartyModule
units, so as long as SubmoduleInitializerService
is injected in the same injector as ThirdPartyService
or parent injector, everything is fine:
export class AppComponent {
constructor(
/* DO NOT REMOVE! BAD THINGS HAPPEN! */
submoduleInitializerService: SubmoduleInitializerService
) {}
...
}
It was proven to be a lousy pattern because it is not obvious why SubmoduleInitializerService
should stay injected in AppComponent
if it's not used neither in class nor in template (was accidentally removed once already).
Basically AppSubmodule
module needs an alternative to Angular 1.x angular.module(...).run(...)
block.
What are the options here?
APP_INITIALIZER
(undocumented) service plays the role of AngularJS config/run blocks reasonably well in Angular 2 (not counting the feature of asynchronous initialization).
For noop intialization block that just eagerly instantiates SubmoduleInitializerService
it is:
@NgModule({
imports: [ThirdPartyModule],
exports: [ThirdPartyModule],
providers: [
ThirdPartyService,
SubmoduleInitializerService,
{
provide: APP_INITIALIZER,
useFactory: () => () => {},
deps: [SubmoduleInitializerService],
multi: true
}
]
})
class AppSubmodule {}
Since APP_INITIALIZER
is multi-provider, it allows to have several initialization functions per application that follow the order in which the modules are being loaded.
For synchronous initialization the shorter (and probably more appropriate) alternative is to inject the service into module's constructor:
@NgModule({
imports: [ThirdPartyModule],
exports: [ThirdPartyModule],
providers: [
ThirdPartyService,
SubmoduleInitializerService
]
})
class AppSubmodule {
constructor(sis: SubmoduleInitializerService) {}
}
As explained in this answer, APP_INITIALIZER
shares some traits with config
block, too, because it is used to configure services prior to component initialization and is susceptible to race conditions (for example, since APP_INITIALIZER
is used to configure Router
, injecting it into another APP_INITIALIZER
will result in circular dependency).
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