AngularInDepth уходит от Medium. Более свежие статьи размещаются на новой платформе inDepth.dev. Спасибо за то, что участвуете в глубоком движении!
Вам когда-нибудь приходилось динамически загружать контент или компоненты в приложения Angular? Как насчет того, чтобы встроенные структурные директивы - * ngIf и * ngSwitch - просто не обеспечивали? Вы также нуждаетесь в преимуществах оптимизации при использовании опережающей компиляции?
Что ж, у меня для вас хорошие новости ... (И нет, вам не обязательно быть Чаком Норрисом!) Если вы следите за обновлениями, я помогу вам запустить и запустить решение, которое обеспечит надежный способ выбора и динамической загрузки во время выполнения - набор предопределенных модулей и компонентов в вашем приложении.
В этой статье предполагается, что вы создаете приложение Angular 6+, созданное с помощью Angular CLI. Для получения информации об использовании Angular CLI ознакомьтесь с официальной документацией.
Это возникло из-за деловой необходимости компании, в которой я работаю. Здесь важно отметить, что существует множество статей и примеров по динамической загрузке контента в Angular, но ни одна из них, которую я обнаружил, не работала надежно при компиляции Angular с включенными флагами `- prod` или` - aot`. Хорошая новость заключается в том, что то, что я описываю в этой статье, прекрасно работает с опережающей компиляцией.
Что мы собираемся делать
Мы собираемся создать специальный модуль с динамическим выходом компонентов, который можно будет включить и использовать в любом месте вашего приложения. Единственное требование - предварительно зарегистрировать массив, сопоставляющий ваши динамические компоненты с их родительскими модулями. Вы также добавите эти модули в свойство lazyModules
в вашем angular.json
файле. Таким образом, компилятор предварительно скомпилирует эти модули. Затем компилятор разделяет их на отдельные минифицированные фрагменты и делает их доступными для загрузчика SystemJS во время выполнения с помощью AOT.
Давайте создадим нашу торговую точку для динамического контента
Предполагая, что у вас есть проект, созданный с помощью интерфейса командной строки Angular 6+, давайте выполним следующие шаги, чтобы сформировать необходимые части, составляющие этот новый выход динамического содержимого.
Создание модуля вывода динамического содержания
Создайте новый модуль с именем DynamicContentOutletModule, выполнив следующую команду в выбранной оболочке:
$ ng g m dynamic-content-outlet
Позже мы вернемся к этому модулю и подключим его.
Создайте реестр выходов динамического содержания
Создайте новый файл под вновь созданной папкой src/app/dynamic-content-outlet
с именем dynamic-content-outlet.registry.ts
. Это будет служить заполнителем для имени компонента, отображающего массив, путь к модулю и имя модуля. На данный момент это будет пустой массив, как показано ниже.
interface RegistryItem { componentName: string; modulePath: string; moduleName: string; } /** * A registry array of Component Name to details * that must be updated with each new component * that you wish to load dynamically. */ export const DynamicContentOutletRegistry: RegistryItem[] = [];
Создайте компонент ошибки выхода динамического содержимого
Создайте новый файл в папке src / app / dynamic-content-output / dynamic-content-output-error.component.ts. Он будет служить компонентом, который будет отображаться каждый раз, когда возникает ошибка при попытке загрузить динамический компонент. Вы можете настроить свойство шаблона для использования любых пользовательских стилей или макета, которые у вас могут быть. Входные данные errorMessage должны оставаться неизменными, и в них будут передаваться фактические сведения об ошибке, возникшей при попытке динамического рендеринга вашего компонента.
Создайте службу распространения динамического контента
Создайте новый файл под папкой src/app/dynamic-content-outlet/dynamic-content-outlet.service.ts
- Эта служба инкапсулирует логику, которая загружает динамические компоненты с помощью SystemJS и отображает их в выходе динамического содержимого.
- Он использует
DynamicContentOutletRegistry
для поиска модуля поcomponentName
. - Он также использует новое свойство
static
, которое мы добавим позже к каждому модулю, который мы хотим динамически загружать, с именемdynamicComponentsMap
. Это позволяет нам получить литерал типа для данногоcomponentName
, чтобыresolveComponentFactory
мог создать экземпляр правильного компонента. Вы можете спросить, почему мы просто не добавили четвертое свойство вDynamicContentOutletRegistry
, потому что, если мы импортируем тип в реестр, это лишает смысла ленивую загрузку этих модулей, поскольку тип будет включен в основной пучок. - Если возникает ошибка, вместо этого отображается
DynamicContentOutletErrorComponent
с включенным сообщением об ошибке.
Создайте компонент выхода динамического содержания
Создайте новый файл в папке src/app/dynamic-content-outlet/dynamic-content-outlet.component.ts
. Этот компонент принимает свойство ввода с именем componentName
, которое будет вызывать DynamicContentOutletService.GetComponent
метод, передаваемый в него componentName
. Затем служба возвращает экземпляр этого визуализированного и скомпилированного компонента для внедрения в представление. Служба возвращает экземпляр компонента ошибки, если по какой-либо причине рендеринг не удается. Компонент отслеживает изменения с помощью ngOnChanges
метода жизненного цикла. Если @Input() componentName: string;
установлен или изменяется, он автоматически повторно отображает компонент по мере необходимости. Он также правильно обрабатывает уничтожение компонента с помощью метода жизненного цикла ngOnDestroy
.
Завершите подключение компонентов к модулю вывода динамического содержимого
Убедитесь, что ваш src/app/dynamic-content-outlet/dynamic-content-outlet.module.ts
файл выглядит следующим образом:
Давайте воспользуемся нашим новым выходом для динамического контента
Уф! Сделайте глубокий вдох и возьмите чашку кофе (органическое темное жаркое из французской прессы). Тяжелая работа позади. Далее мы рассмотрим процесс ввода в действие этого нового модуля!
Для любого компонента, который вы хотите визуализировать динамически, вам необходимо выполнить следующие четыре шага. Эти шаги необходимо выполнять точно.
1. Подготовьте свой модуль к динамическому импорту.
- Убедитесь, что компонент указан в массиве
entryComponents
в модуле, частью которого является компонент. - Добавьте в модуль новое свойство
static
с именемdynamicComponentsMap
. Это позволяет нам получить литерал типа для данногоcomponentName
, чтобыresolveComponentFactory
мог создать экземпляр правильного компонента.
Вы можете спросить, почему мы просто не добавили четвертое свойство к
DynamicContentOutletRegistry
с именемcomponentType
; ну, это потому, что если мы импортируем тип в реестр, то это лишает смысла ленивую загрузку этих модулей, поскольку тип будет включен в основной пакет.
Подготовленный модуль может выглядеть следующим образом:
2. Добавьте динамический компонент (ы) в реестр.
Для любого компонента, который вы хотите динамически отображать, добавьте новую запись в массив DynamicContentOutletRegistry
в src/app/dynamic-content-outlet/dynamic-content-outlet.registry.ts
.
Необходимо заполнить следующие свойства:
- componentName: оно должно точно соответствовать имени компонента, который вы хотите загружать динамически.
- modulePath: абсолютный путь к модулю, содержащему компонент, который вы хотите загружать динамически. Это только путь к модулю и НЕ включает имя модуля после
#
. - moduleName: точное имя модуля.
Пример сопоставления компонентов
{ componentName: 'MySpecialDynamicContentComponent', modulePath: 'src/app/my-special-dynamic-content/my-special-dynamic-content.module', moduleName: 'MySpecialDynamicContentModule' },
3. Добавьте свои динамические модули в массив lazyModules.
В вашем angular.json
обновите массив projects > ** > architect > build > options > lazyModules
и добавьте элемент для каждого модуля, который вы добавили в реестр, чтобы компилятор Angular AOT обнаружил и предварительно скомпилировал ваши динамические модули. Если у вас есть несколько проектов в папке, убедитесь, что вы добавили это для правильного проекта, который вы импортируете и в котором используете динамические модули. Обновленный файл будет выглядеть примерно так:
{ ... "projects": { "angular-dynamic-content": { ... "architect": { "build": { "builder": "@angular-devkit/build-angular:browser", "options": { ... "lazyModules": ["src/app/my-special-dynamic-content/my-special-dynamic-content.module"] }, } } } } }
Подключите модуль вывода динамического содержимого
К этому моменту вы создали модуль выхода динамического содержимого и зарегистрировали свои компоненты, чтобы они были доступны в розетке. Последнее, что нам нужно сделать, это подключить наш новый DynamicContentOutletModule
для использования в нашем приложении. Для этого вам необходимо:
1. Добавьте ваш новый DynamicContentOutletModule
в imports
array любого функционального модуля или главного AppModule
вашего приложения Angular.
Пример добавления в массив `import`
@NgModule({ ... imports: [ ... DynamicContentOutletModule ], ... }) export class AppModule {}
2. Добавьте следующий тег в шаблон родительского компонента, в котором вы хотите отображать динамическое содержимое:
<app-dynamic-content-outlet [componentName]="'MyComponent'"> </app-dynamic-content-outlet>
По своей природе это очень похоже на встроенный в Angular тег <router-outlet>/</router-outlet>
.
3. Счастливого ng serve --prod
ing!
Заключение
Надеюсь, вы нашли это решение полезным. Вот полный пример репозитория GitHub, который вы можете клонировать и поиграть. PR приветствуются, ценятся, поощряются и принимаются!
Реальный сложный пример
Если вас интересует более подробный пример из реальной жизни, загляните в репозиторий Github, который продемонстрирует следующее:
- Динамические модули с несколькими компонентами
- Демонстрация использования изменений компонентов на лету
- Демонстрация того, что стили с заданной областью видимости загружаются динамически для каждого компонента.
Пример репозитория GitHub
Дополнительные ресурсы
Я очень рекомендую записаться на курсы Ultimate Angular. Он стоит своих денег, и я использовал его как обучающий инструмент для новых разработчиков Angular. Перейдите по ссылке ниже, чтобы зарегистрироваться.
Особая благодарность
Я хочу воспользоваться моментом и поблагодарить всех, у кого мне удалось почерпнуть эту информацию. Я не придумал все это самостоятельно, но смог получить рабочее решение, объединив части из каждой из этих статей!
Кроме того, огромное спасибо читателю Medium ivanwonder и пользователю Github Милану Сарайе за указание на это и предоставление примера разрешения вилки.