Angular App динамически загружает плагин без перекомпиляции

Я пытаюсь разработать интерфейс своего подключаемого приложения Web Api (NET CORE). Я хотел бы использовать Angular 9, но я не эксперт в angular.

Мой бэкэнд был разработан с возможностью расширения, и при запуске он наблюдает за указанной папкой, и если существует один или несколько файлов DLL, содержащих логику для расширения базового приложения (например, плагина), он загружает их. Я хочу использовать аналогичный подход во внешнем интерфейсе. Я пробовал разные решения и читал много статей, но мне трудно найти кого-то, кто захочет импортировать неизвестный плагин во время компиляции.

Я пробовал ленивые модули (начиная с этого: https://www.mokkapps.de/blog/manually-lazy-load-modules-and-components-in-angular/), которые были бы идеальными, но, используя это, я должен знать, что реализовано plugin (modules) перед компиляцией моего приложения angular, потому что, если я хочу использовать модули, я должен использовать функцию импорта в моем основном приложении.

Поэтому я искал больше, и после статьи " Загружать новые модули динамически во время выполнения с помощью Angular CLI и Angular 5" я попробовал подход System.Js, но не могу найти рабочего решения для angular 9.

Я почти уверен, что я не единственный, кто создал бы подключаемое приложение Angular, которое загружает плагины без перекомпиляции основного приложения.

Мне нужны предложения о правильном подходе или рабочий пример приложения angular, использующего архитектуру плагинов.

1 ответ

Я не уверен, что это лучшее и элегантное решение, потому что я новичок в Angular, но на данный момент оно работает для меня.

Я предполагаю, что плагин - это веб-компонент Element (https://angular.io/guide/elements). Чтобы создать свой первый элемент (плагин), я следовал этому руководству: https://www.techiediaries.com/angular/angular-9-elements-web-components/.

Кстати, сейчас я не могу загрузить свои плагины динамически, потому что мне нужно знать имя компонента, который я развернул в своем элементе, прежде чем скомпилировать проект для его использования. Я нашел решение, используя элемент Extensions (https://angular-extensions.github.io/elements/). Итак, я создал динамический компонент, который использую, чтобы показать компонент плагина во время выполнения.

Это код динамического компонента:

export class DynamicComponentContainerComponent implements OnInit {
  plugin: Plugin
  sub: any;

  constructor(private route: ActivatedRoute, private pluginService: PluginService) { }

  ngOnInit() {
    this.sub = this.route
      .data
        .subscribe(data => {
           this.pluginService.getPlugin(data['pluginName'])
            .subscribe(
              (res) => {
                this.plugin = res;
              },
              (err) => {
                console.error(err)
              } 
            );
          });
  }

  ngOnDestroy() {
    this.sub.unsubscribe();
  }
}

И его шаблон html:

<div *ngIf="plugin != null">
<ng-template #loading>Loading plugin {{plugin.tag}} ...</ng-template>
<ng-template #error>Error Loading plugin {{plugin.tag}}</ng-template>
<ax-lazy-element
    *axLazyElementDynamic="plugin.tag; url: plugin.url; module: plugin.isModule; 
errorTemplate: error; loadingTemplate: loading;">
</ax-lazy-element>
</div>

Это может работать, потому что мой бэкэнд обслуживает скомпилированный файл JS плагина (веб-компонент элемента), поэтому мне пришлось зарегистрировать свой плагин (потому что мне нужны некоторые значения для его обработки, такие как имя компонента или путь маршрута) перед его использованием. Фактически, атрибуту axLazyElementDynamic в динамическом компоненте для работы нужен URL-адрес файла веб-компонента JS Element и имя компонента.

Теперь мне пришлось динамически добавить пути маршрута к каждому компоненту плагина. В моем компоненте приложения я создал этот простой метод:

loadPlugins() {
  this.pluginService.getPlugins()
    .subscribe(plugins => {
      plugins.forEach(plugin => {
        this.router.config.unshift({ path: plugin.path, component: 
DynamicComponentContainerComponent, data: { pluginName: plugin.name } });
        this.links.push({ text: plugin.description, path: plugin.path });
      });
    });
}

Служба плагинов просто получает данные плагинов из серверной части (где я регистрировал плагины раньше).

Надеюсь, это может кому-то помочь.

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