Итак, что я пытаюсь сделать, так это загрузить модули или компоненты, которые неизвестны во время компиляции.
Мы (моя команда и я) создали приложение, и теперь мы хотим сделать его расширяемым для других команд. Мы хотим, чтобы команды могли просто создавать свои собственные компоненты и загружать их во время выполнения.
Итак, что я придумал, так это то, что команда просто регистрирует URL-адрес файла JavaScript, и наше приложение динамически импортирует этот файл JavaScript. Между прочим, я все еще нахожусь в своего рода PoC-фазе, и я не могу показать вам, как я решил эту проблему в реальном приложении, поэтому я создаю минимальное приложение, которое делает то же самое.
yenteo/angular-dynamic-imports
Итак, у меня есть основное приложение, которое является обычным приложением Angular 10. В этом приложении у меня есть NormalComponent и InternalAngularElementsComponent. NormalComponent используется в app.component.ts. InternalAngularElementsComponent регистрируется как настраиваемый элемент и добавляется в HTML-код AppComponent.
Теперь это все работает корректно. Если вы просто сделаете npm start
в папке основного приложения, приложение Angular запустится по адресу http://localhost:4200, и вы сможете нажимать кнопки компонентов, и вы увидите, что значение на кнопке увеличивается на 1 каждый клик .
Есть второе приложение, которое называется external-application. Это простой проект TypeScript с Rollup.js. Я определяю компонент, аналогичный двум другим, и регистрирую его в модуле. Я использую Rollup.js, поэтому я получаю модуль CommonJS, который я могу динамически импортировать в свое основное приложение (внутри app.component). При запуске npm build
внутри внешнего приложения он скомпилирует все в src/index.ts в dist/index.js и скопирует этот файл в assets/external/index.js в основном приложении.
В app.component.ts в AfterViewInit делаем динамический импорт этого файла и вызываем экспортируемую функцию activate
импортированного модуля. Эта функция делает следующее:
- Он определяет модуль Angular и импортирует наш модуль, определенный выше, который объявляет наш компонент.
- Он загружает этот модуль с помощью
platformBrowserDynamic().bootstrapModule(ExtensionModule)
- Он разрешает фабрику компонентов для компонента, который мы зарегистрировали.
- Он регистрирует компонент как Angular Element
Теперь, когда я использую этот Angular Element, компонент работает, но у меня все еще есть некоторые проблемы. Я бы предпочел иметь возможность передать существующий инжектор моего основного приложения, чтобы я мог использовать каждого определенного там провайдера. Теперь вы можете видеть, что я сделал некоторые вещи, чтобы внедрить DataService во внешний компонент. Это работает, но шаблон не обновляется правильно. Я предполагаю, что это потому, что я создаю новый контекст Angular с platformBrowserDynamic().bootstrapModule(...)
.
Я попытался использовать компилятор существующего контекста Angular следующим образом:
compiler.compileModuleAndAllComponentsAsync(ExtensionModule);
но это всегда приводит к ошибкам в ExtensionModule с «Невозможно прочитать объявления свойств» с нулевым значением.
Я пробовал SystemJsNgModuleLoader, но это кажется устаревшим, потому что компилятор говорит использовать оператор import
вместо загрузчика, но при использовании оператора импорта будет использоваться веб-пакет, и для AFAIK потребуется, чтобы код присутствовал во время компиляции.
Есть ли у кого-нибудь опыт в этом или знания о том, как это сделать правильно?