Reputation: 1322
I am currently using Angular 4's asynchronous compiler to dynamically load an external module and add one of its components into the DOM using NgComponentOutlet. The external module is meant to be functionally disjoint from the main application, and as such it should have its own, unique dependency injector (or rather, an injector without access to the providers of the parent module). However, if I create a new dependency injector using ReflectiveInjector.resolveAndCreate
, NgComponentOutlet fails with the error No provider for NgModuleRef!
.
After some tracing through NgComponentOutlet, this seems to be because the NgModuleFactory depends on renderers, etc. in order to create the component. However, I've been unable to find an API that exposes a minimal dependency injector akin to the one provided to the root module. Is there any way to obtain this injector, or to create a minimal dependency injector that still contains the necessary resources for rendering a dynamically compiled component?
Upvotes: 1
Views: 188
Reputation: 105547
It's not possible to restrict access to the application injector because it contains application wide global providers like ApplicationRef
, ErrorHandler
, rendererFactory
etc. Also, it's impossible to get only the injector with these providers because all module providers are merged. Read more in this answer.
However, it is possible to restrict access to the providers defined by the parent components. Angular uses different kind of injectors for the components - see this answer. To do that you need to pass into the module factory the application injector instead of the injector you obtain through dependency injector into the component constructor. The only way I know now to get this application injector is like this:
platform.bootstrapModule(AppModule).then((module) => {
window['rootInjector'] = module.injector;
});
There is also platform injector that you can easily obtain but it's useless for our purposes. Read more in this answer. So assume that you have obtained the application injector you can then pass it to the module factory:
System.import('app/b.module').then((module) => {
const appInjector = window['rootInjector'];
const moduleFactory_ = c.compileModuleSync(module.BModule);
const moduleRef = moduleFactory_.create(appInjector);
Or the way you do it with ngComponentOutlet
.
The other possible approach is to get the application wide dependencies and remap them inside the custom injector:
const applicationRef = i.get(ApplicationRef);
const errorHandler = i.get(ErrorHandler);
...
const customInjector = ReflectiveInjector.resolveAndCreate([
{provide: ApplicationRef, useValue: applicationRef},
{provide: ErrorHandler, useValue: errorHandler}
...
]);
But there's a big likelihood for it to fail some time in the future.
Upvotes: 1