我有以下Angular代码:
dashboard.component.ts
import {Component, AfterViewInit, ComponentFactoryResolver, ViewContainerRef, OnInit, Input} from "@angular/core";
import {WidgetItem} from "../modules/widget-item";
import {WidgetComponent} from "../modules/widget.component";
import {DashboardService} from "./dashboard.service";
@Component({
selector: 'dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.css']
})
export class DashboardComponent implements OnInit, AfterViewInit {
@Input() widgets: WidgetItem[];
constructor(
private dashboardService: DashboardService,
private componentFactoryResolver: ComponentFactoryResolver,
private viewContainerRef: ViewContainerRef
) {}
ngOnInit(): void {
this.widgets = this.dashboardService.getWidgets();
}
ngAfterViewInit(): void {
this.loadDashboard();
}
private loadDashboard(): void {
this.viewContainerRef.clear();
if(this.widgets) {
for (let widget of this.widgets) {
let componentFactory = this.componentFactoryResolver.resolveComponentFactory(widget.component);
let componentRef = this.viewContainerRef.createComponent(componentFactory);
(<WidgetComponent>componentRef.instance).data = widget.data;
}
}
}
}
和dashboard.service.ts
import {Injectable} from '@angular/core';
import {WidgetItem} from "../modules/widget-item";
@Injectable()
export class DashboardService {
private dashboardWidgets: WidgetItem[];
constructor() {
this.dashboardWidgets = [];
}
getWidgets() {
console.log('get widgets');
return this.dashboardWidgets;
}
addWidget(widget: WidgetItem): void {
console.log('add widget', widget);
this.dashboardWidgets.push(widget);
}
}
和contract-widget.component.ts
import {Component} from "@angular/core";
import {WidgetComponent} from "./widget.component";
@Component({
selector: 'contract-widget',
templateUrl: './contract-widget.component.html'
})
export class ContractWidgetComponent implements WidgetComponent {
data: any;
}
和widget.component.ts
export interface WidgetComponent {
data: any;
}
和widget-item.ts
import {Type} from "@angular/core";
export class WidgetItem {
constructor(public component: Type<any>, public data:any) {}
}
我尝试做的是使用componentfactoryresol在我的仪表板上动态加载小部件。当我手动将ContractWidgetComponent添加到我的app.component entryComponents时,这非常有效。但是,我想要完成的是我可以将小部件加载到我的仪表板上而不知道它们存在于我的应用程序中。我已将代码分开,因此我的文件夹结构如下所示:
/app -- contains app.module.ts, app-routing.module.ts and app.component.ts
/app/parts -- contains dahsboard.component.ts and dahsboard.service.ts
/app/modules -- contains contract-widget.component.ts widget.component.ts and widget-item.ts
在我的小部件中,我还设置了一个组件,因此我可以将组件用作loadChildren来动态加载组件。 (在我的应用程序/模块中,我确实有更多代码与其他组件和服务一起用于解释我的问题所不需要的特定功能。)
我想要做的是能够在我的/ app / modules中开发另一个模块,并在那里添加所需的文件,让我的仪表板能够在我的模块中显示任何新的小部件,而无需更改我的/中的代码app或/ app / parts目录。
主要部分可以正常工作,但当我尝试使用loadDashboard()
在我的信息中心上加载小部件时,我收到以下错误:
ERROR Error: Uncaught (in promise): Error: No component factory found for ContractWidgetComponent. Did you add it to @NgModule.entryComponents?
Error: No component factory found for ContractWidgetComponent. Did you add it to @NgModule.entryComponents?
这是有道理的,因为我不想将ContractWidgetComponent添加到我的/app/app.module.ts entryComponents数组,因为我的应用程序不知道我将拥有哪些类型的小部件(将来)。
有没有办法以编程方式将ContractWidgetComponent添加到运行时上的entryComponent,以便我可以在我的仪表板上动态加载我的小部件?
非常感谢任何帮助。
答案 0 :(得分:1)
我想做的是能够在我的内部开发另一个模块 / app / modules,只需在那里添加所需的文件并拥有我的仪表板 只需在我的模块中显示任何新的小部件而无需更改 我的/ app或/ app / parts目录中的代码。
是的,你可以这样做。只需定义模块声明中指定contract-widget.component.ts
的模块,动态加载该模块并使用compileModuleAndAllComponentsAsync
编译器API生成模块的所有工厂。然后,您可以访问这些工厂并动态实例化组件。
有关详情,请参阅Here is what you need to know about dynamic components in Angular