Storing injector instance for use in components
For today's TypeScript and Angular 5, avoiding WARNING in Circular dependency detected
when importing the global injector, first declare a helper, e.g. app-injector.ts
:
import {Injector} from '@angular/core';
/**
* Allows for retrieving singletons using `AppInjector.get(MyService)` (whereas
* `ReflectiveInjector.resolveAndCreate(MyService)` would create a new instance
* of the service).
*/
export let AppInjector: Injector;
/**
* Helper to set the exported {@link AppInjector}, needed as ES6 modules export
* immutable bindings (see http://2ality.com/2015/07/es6-module-exports.html) for
* which trying to make changes after using `import {AppInjector}` would throw:
* "TS2539: Cannot assign to 'AppInjector' because it is not a variable".
*/
export function setAppInjector(injector: Injector) {
if (AppInjector) {
// Should not happen
console.error('Programming error: AppInjector was already set');
}
else {
AppInjector = injector;
}
}
Next, in your AppModule
, set it using:
import {Injector} from '@angular/core';
import {setAppInjector} from './app-injector';
export class AppModule {
constructor(injector: Injector) {
setAppInjector(injector);
}
}
And wherever needed, use:
import {AppInjector} from './app-injector';
const myService = AppInjector.get(MyService);
Another solution with angular 2.0.0 final :
platformBrowserDynamic().bootstrapModule(AppModule, [
{
defaultEncapsulation: ViewEncapsulation.Emulated,
providers: [
{ provide: TRANSLATIONS, useValue: TRANSLATION },
{ provide: TRANSLATIONS_FORMAT, useValue: 'xlf' },
{ provide: LOCALE_ID, useValue: 'fr' }
]
}
]).then((modref: NgModuleRef<any>) => {
appInjector(modref.injector);
});
Class based solution.
Often I need to refer to a service from classes that are used by other classes. Injecting services via a constructor is cumbersome and causes issues for the calling classes, which do not need the given service(s).
In Angular 8, I setup a library class: ServiceInjectorModule (can also be used inside sub-modules)
(derived from similar answers on stackoverflow)
File: service-injector.module.ts
import { NgModule, Injector } from '@angular/core';
export let ServiceInjector: Injector;
@NgModule()
export class ServiceInjectorModule {
constructor(private injector: Injector) {
ServiceInjector = this.injector;
}
}
File: my-lib.module.ts
Note: You can skip this library module as it is only more convenient if you have other services or modules to use. If you skip it, import ServiceInjectorModule directly to your AppModule.
import { ServiceInjectorModule } from './service-injector.module';
const impExpModules = [
...,
ServiceInjectorModule,
...
];
@NgModule({
imports: [impExpModules],
exports: [impExpModules]
})
export class MyLibModule {}
Import MyLibModule to your AppModule or where best fits.
Now in your components or classes, simply:
import { ServiceInjector } from '../modules/lib/service-injector.module';
import { MyService } from '../services/my.service';
export class MyCurrentClass {
myService = ServiceInjector.get(MyService);
...
myLocalFunction() {
...
this.myService.myServiceFunction(...
}
}
I've managed to do it using manual boostrapping. Don't use "bootstrap: [AppComponent]
" declaration in @NgModule
, use ngDoBootstrap
method instead:
export class AppModule {
constructor(private injector: Injector) {
}
ngDoBootstrap(applicationRef: ApplicationRef) {
ServiceLocator.injector = this.injector;
applicationRef.bootstrap(AppComponent);
}
}