Reputation: 8928
Before RC5 I was using appref injector as a service locator like this:
Startup.ts
bootstrap(...)
.then((appRef: any) => {
ServiceLocator.injector = appRef.injector;
});
ServiceLocator.ts
export class ServiceLocator {
static injector: Injector;
}
components:
let myServiceInstance = <MyService>ServiceLocator.injector.get(MyService)
Now doing the same in bootstrapModule().then() doesn't work because components seems to start to execute before the promise.
Is there a way to store the injector instance before components load?
I don't want to use constructor injection because I'm using the injector in a base component which derived by many components and I rather not inject the injector to all of them.
Upvotes: 47
Views: 23650
Reputation: 1810
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(...
}
}
Upvotes: 1
Reputation: 23559
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);
Upvotes: 75
Reputation: 379
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);
});
Upvotes: 3
Reputation: 8928
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);
}
}
Upvotes: 12