Как добавить элементы в среду выполнения NgModule entryComponents?
У меня есть следующий фрагмент кода 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) {}
}
То, что я пытаюсь сделать, это динамически загрузить виджет на моей панели, используя компонент factoryresolver. Это прекрасно работает, когда я вручную добавляю 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 во время выполнения, чтобы я мог динамически загружать свой виджет на свою панель?
Любая помощь приветствуется.
1 ответ
То, что я хочу сделать, - это иметь возможность разрабатывать другой модуль внутри моего / app / modules и просто добавлять туда необходимые файлы, и моя панель инструментов может отображать любой новый виджет в моих модулях без изменения кода в моем / app или / каталог app / parts.
Да, вы можете сделать это. Просто определите модуль с contract-widget.component.ts
указанный в объявлениях модуля, загружать этот модуль динамически и использовать compileModuleAndAllComponentsAsync
API компилятора для генерации всех фабрик модуля. Затем вы можете получить доступ к этим фабрикам и создавать экземпляры компонентов динамически.
Для получения более подробной информации читайте Вот что вам нужно знать о динамических компонентах в Angular