Reputation: 115
I'm using angular cli and angular 4. I've made some classes from which my components inherit behavivour! However during development their constructores grow and in the current phase I need to be able to allow other to use them whithout the need of knowing their constructors!
So after seeing Petr sugestion in dependency injection I tried, but had to make some adjustments due to errors the compiler was giving, I suppose due to diferences betwween angular 4 and angular 5!
This is my code:
Service Locator:
import {ReflectiveInjector} from "@angular/core";
export class ServiceLocator {
static injector: ReflectiveInjector;
}
module which imports the serviceLocator:
ServiceLocator.injector = ReflectiveInjector.resolveAndCreate(
Object.keys(services).map(key => ({
provide: services[key].provide,
useClass: services[key].provide,
deps: services[key].deps
}))
);
serviceList:
import {SysFwDatesService} from '../sysDates/sysFwDates.service';
import {MatSnackBar} from '@angular/material';
import {SysFwFormSpecBuilder} from '../uiValidation/SysFwFormSpecBuilder';
import {SysFwHttpApi} from '../http/SysFwHttpApi';
export const services: {[key: string]: {provide: any, deps: any[], useClass?: any}} = {
'SysFwDatesService': {
provide: SysFwDatesService,
deps: []
},
'MatSnackBar': {
provide: MatSnackBar,
deps: []
},
'SysFwFormSpecBuilder': {
provide: SysFwFormSpecBuilder,
deps: []
},
'SysFwHttpApi': {
provide: SysFwHttpApi,
deps: []
}
}
It seems to be working, however it seems to have lost the other providers and to be expecting all providers to be passed this way!
This is the error I'm getting:
No provider for HttpClient! (SysFwDatesService -> SysFwHttpApi -> HttpClient)
Do I need to put everything in the servicesList? What am I doing wrong?
Before I use the injector it all worked fine!
Thanks for your help!
Upvotes: 4
Views: 1807
Reputation: 222369
HttpClient
is defined in HttpClientModule
and has a hierarchy of dependencies that is not very easy to list by hand as single providers
array. ReflectiveInjector
and Injector
don't support Angular modules, and it's impractical to parse a module to get a hierarchy providers
manually - this is what Angular already does internally.
The module that initializes service locator should import dependency modules, too, and resulting injector should inherit from module injector:
@NgModule({ imports: [HttpClientModule], ... })
export class AppModule {
constructor(injector: Injector) {
ServiceLocator.injector = ReflectiveInjector.resolveAndCreate(
Object.keys(services).map(key => ({
provide: services[key].provide,
useClass: services[key].provide,
deps: services[key].deps
})),
injector
);
}
}
By the way, services
object keys aren't used for anything and are redundant. services
likely should be an array.
I would strongly advise against using this home-grown service locator in real-world application. It is a proof of concept that does what it does, but also a hack that isn't idiomatic to the framework and may have a lot of negative consequences that may not be obvious at this moment, e.g. lazy loaded modules.
The recommendation is to use the means offered by the framework wherever possible, this way the application has most chances to be trouble-free in future.
It is:
@Injectable() // needed for JIT compilation
abstract class Injective {
constructor(protected injector: Injector) {}
}
@Injectable() // potentially needed for AOT compilation
class FooService extends Injective {
baz = this.injector.get(Baz);
}
@Component(...)
class BarComponent extends Injective {
foo = this.injector.get(FooService);
// just passes injector to parent constructor if there's a need for own constructor
constructor(injector: Injector, public baz: Baz) {
super(injector);
// ...
}
}
Notice that to be on safe side, @Injectable()
is needed on both parent and child classes (this may vary between Angular and Angular CLI versions).
Upvotes: 1
Reputation: 3141
have you tried explicitly specifying HttpClient
as a dependency if SysFwHttpApi
? Like
'SysFwHttpApi': {
provide: SysFwHttpApi,
deps: [HttpClient]
}
Upvotes: 0