角度8中的延迟加载模块 [英] Lazy load module in angular 8
问题描述
我有一个仪表板应用程序,在这里我懒惰地加载小部件(与路线无关).
我通过定义对象{name: string, loadChildren: string}
来做到这一点.然后在我的app.module
中,我将执行provideRoutes(...)
.
这将导致cli为每个小部件模块创建一个块.
然后在运行时,我将使用SystemJsModuleLoader
加载该字符串并获得一个NgModuleRef
.
我可以从模块中创建组件,然后在ViewContainerRef
上调用createComponent
.
以下是该功能:
loadWidget(
name: string,
container: ViewContainerRef,
widget: Widget
): Promise<{ instance: WidgetComponent; personlize?: { comp: any; factory: ComponentFactoryResolver } }> {
if (this.lazyWidgets.hasOwnProperty(name)) {
return this.loader.load(this.lazyWidgets[name]).then((moduleFactory: NgModuleFactory<any>) => {
const entryComponent = (<any>moduleFactory.moduleType).entry;
const moduleRef = moduleFactory.create(this.injector);
const compFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(entryComponent);
const comp = container.createComponent(compFactory);
(<WidgetComponent>comp.instance).widget = widget;
const personalize = (<any>moduleFactory.moduleType).personalize;
if (personalize) {
return {
instance: <WidgetComponent>comp.instance,
personlize: {
comp: personalize,
factory: moduleRef.componentFactoryResolver,
injector: moduleRef.injector
}
};
} else {
return {
instance: <WidgetComponent>comp.instance
};
}
});
} else {
return new Promise(resolve => {
resolve();
});
}
在角度8中,loadChildren
更改为导入功能.
您将获得实际的模块实例,而不是NgModuleRef
.
我认为我可以通过采用该模块来对其代码进行修复,对其进行编译以获得NgModuleRef
,然后将其余代码保持相同.
尽管编译器未捆绑,但似乎在AOT模式下.
因此,我现在基本上只能使用所需的组件实例,但是无法将其添加到View容器中.
它需要一个我无法获得的组件工厂解析器.
我想我的问题是如何获取一个组件实例并将其添加到角度8的视图容器中.现在我已经恢复为使用字符串版本的loadChildren,但这只能在版本9发布之前起作用./p>
这是编译器无法在AOT中使用的版本
if (this.lazyWidgets.hasOwnProperty(name)) {
return this.lazyWidgets[name]().then((mod: any) => {
const moduleFactory = this.compiler.compileModuleSync(mod);
const entryComponent = (<any>moduleFactory.moduleType).entry;
const moduleRef = moduleFactory.create(this.injector);
const compFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(entryComponent);
const comp = container.createComponent(compFactory);
(<WidgetComponent>comp.instance).widget = widget;
const personalize = (<any>moduleFactory.moduleType).personalize;
if (personalize) {
return {
instance: <WidgetComponent>comp.instance,
personlize: {
comp: personalize,
factory: moduleRef.componentFactoryResolver,
injector: moduleRef.injector
}
};
}
这是我正在考虑如何执行但又无法将其添加到ViewContainerRef
的示例.
模块实例实现了一个需要"entry"属性的接口.
这定义了要加载的实际组件:
if (this.lazyWidgets.hasOwnProperty(name)) {
return this.lazyWidgets[name]().then((mod: any) => {
const comp = mod.entry;
(<WidgetComponent>comp.instance).widget = widget;
const personalize = mod.personalize;
if (personalize) {
return {
instance: <WidgetComponent>comp.instance,
personlize: {
comp: personalize,
factory: null //this no longer works: moduleRef.componentFactoryResolver,
// injector: moduleRef.injector
}
};
} else {
return {
instance: <WidgetComponent>comp.instance
};
}
});
}
我试图在stackblitz中添加一个示例,但是编译器正在将我的字符串转换为函数.至少代码更具可读性.这是我在angular 8中所做的.我基本上需要一种方法来使用import()
而不是魔术弦.
https://stackblitz.com/edit/angular-9yaj4l
在Angular 8中,loadChildren
函数的结果在JIT模式下为NgModule
类型的Promise
或在AOT中为NgModuleFactory
类型的Promise
模式.
考虑到这一点,您可以按以下方式重写服务:
import {
Injectable, Compiler, Injector, Type,
ViewContainerRef, ComponentFactoryResolver,
NgModuleFactory, Inject
} from '@angular/core';
@Injectable()
export class LazyLoaderService {
constructor(private injector: Injector,
private compiler: Compiler,
@Inject(LAZY_WIDGETS) private lazyWidgets:
{ [key: string]: () => Promise<NgModuleFactory<any> | Type<any>> }) { }
async load(name: string, container: ViewContainerRef) {
const ngModuleOrNgModuleFactory = await this.lazyWidgets[name]();
let moduleFactory;
if (ngModuleOrNgModuleFactory instanceof NgModuleFactory) {
// aot mode
moduleFactory = ngModuleOrNgModuleFactory;
} else {
// jit mode
moduleFactory = await this.compiler.compileModuleAsync(ngModuleOrNgModuleFactory);
}
const entryComponent = (<any>moduleFactory.moduleType).entry;
const moduleRef = moduleFactory.create(this.injector);
const compFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(entryComponent);
const comp = container.createComponent(compFactory);
}
}
提示: 如有疑问,请始终查看源代码
-
loadWidget( name: string, container: ViewContainerRef, widget: Widget ): Promise<{ instance: WidgetComponent; personlize?: { comp: any; factory: ComponentFactoryResolver } }> { if (this.lazyWidgets.hasOwnProperty(name)) { return this.loader.load(this.lazyWidgets[name]).then((moduleFactory: NgModuleFactory<any>) => { const entryComponent = (<any>moduleFactory.moduleType).entry; const moduleRef = moduleFactory.create(this.injector); const compFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(entryComponent); const comp = container.createComponent(compFactory); (<WidgetComponent>comp.instance).widget = widget; const personalize = (<any>moduleFactory.moduleType).personalize; if (personalize) { return { instance: <WidgetComponent>comp.instance, personlize: { comp: personalize, factory: moduleRef.componentFactoryResolver, injector: moduleRef.injector } }; } else { return { instance: <WidgetComponent>comp.instance }; } }); } else { return new Promise(resolve => { resolve(); }); }
In angular 8 the
loadChildren
changes to the import function.Instead of an
NgModuleRef
you get the actual module instance.I thought I could fix my code by taking that module, compiling it to get the
NgModuleRef
then keeping the rest of the code the same.It seems that in AOT mode though the compiler does not get bundled.
So I am basically stuck now with an instance of the component I need but no way to add it to the View container.
It requires a component factory resolver which I can't get.
I guess my question is how to take an instance of a component and add it to view container in angular 8. For now I have reverted to using the string version of loadChildren but that will only work until version 9 comes out.
Here is the version with the compiler that does not work in AOT
if (this.lazyWidgets.hasOwnProperty(name)) { return this.lazyWidgets[name]().then((mod: any) => { const moduleFactory = this.compiler.compileModuleSync(mod); const entryComponent = (<any>moduleFactory.moduleType).entry; const moduleRef = moduleFactory.create(this.injector); const compFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(entryComponent); const comp = container.createComponent(compFactory); (<WidgetComponent>comp.instance).widget = widget; const personalize = (<any>moduleFactory.moduleType).personalize; if (personalize) { return { instance: <WidgetComponent>comp.instance, personlize: { comp: personalize, factory: moduleRef.componentFactoryResolver, injector: moduleRef.injector } }; }
And here is an example of how I was thinking to do it but then having no way to add it to the
ViewContainerRef
.The module instance implements an interface that requires an 'entry' property.
This defines the actual component to load:
if (this.lazyWidgets.hasOwnProperty(name)) { return this.lazyWidgets[name]().then((mod: any) => { const comp = mod.entry; (<WidgetComponent>comp.instance).widget = widget; const personalize = mod.personalize; if (personalize) { return { instance: <WidgetComponent>comp.instance, personlize: { comp: personalize, factory: null //this no longer works: moduleRef.componentFactoryResolver, // injector: moduleRef.injector } }; } else { return { instance: <WidgetComponent>comp.instance }; } }); }
EDIT:
I tried to add an example in stackblitz but the compiler is turning my string to functions. At least the code is more readable. this is what I was doing in angular 8. I basically need a way to do this with
import()
instead of magic string.https://stackblitz.com/edit/angular-9yaj4l
解决方案In Angular 8 the result of
loadChildren
function is eitherPromise
ofNgModule
type in JIT mode orPromise
ofNgModuleFactory
in AOT mode.With this in mind you can rewrite your service as follows:
import { Injectable, Compiler, Injector, Type, ViewContainerRef, ComponentFactoryResolver, NgModuleFactory, Inject } from '@angular/core'; @Injectable() export class LazyLoaderService { constructor(private injector: Injector, private compiler: Compiler, @Inject(LAZY_WIDGETS) private lazyWidgets: { [key: string]: () => Promise<NgModuleFactory<any> | Type<any>> }) { } async load(name: string, container: ViewContainerRef) { const ngModuleOrNgModuleFactory = await this.lazyWidgets[name](); let moduleFactory; if (ngModuleOrNgModuleFactory instanceof NgModuleFactory) { // aot mode moduleFactory = ngModuleOrNgModuleFactory; } else { // jit mode moduleFactory = await this.compiler.compileModuleAsync(ngModuleOrNgModuleFactory); } const entryComponent = (<any>moduleFactory.moduleType).entry; const moduleRef = moduleFactory.create(this.injector); const compFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(entryComponent); const comp = container.createComponent(compFactory); } }
Tip: Always look at the source code when you're in doubt
这篇关于角度8中的延迟加载模块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!