Как добавить элементы в среду выполнения 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

Другие вопросы по тегам