我有一个仪表板应用程序,在这里我懒惰地加载小部件(与路线无关)。
我通过定义对象{name: string, loadChildren: string}
来做到这一点。然后在我的app.module
中,我将做provideRoutes(...)
。
这将导致cli为每个小部件模块创建一个块。
然后在运行时,我将使用SystemJsModuleLoader
加载该字符串并获得一个NgModuleRef
。
使用我可以从模块创建组件,然后在createComponent
上调用ViewContainerRef
。
以下是该功能:
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()
而不是魔术字符串的方法。
答案 0 :(得分:5)
在Angular 8中,loadChildren
函数的结果在JIT模式下为Promise
类型的NgModule
在AOT模式下为Promise
的{{1}}。
考虑到这一点,您可以按以下方式重写服务:
NgModuleFactory
提示: 如有疑问,请始终查看源代码
答案 1 :(得分:0)
似乎在使用路由器时,lazyWidgets
const应该不具有name
而是具有path
属性:
export const lazyWidgets: { path: string, loadChildren: () => .....
否则,您将得到错误:
路由“”的配置无效:路由必须具有路径或 指定匹配器