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 --proding!

Заключение

Надеюсь, вы нашли это решение полезным. Вот полный пример репозитория GitHub, который вы можете клонировать и поиграть. PR приветствуются, ценятся, поощряются и принимаются!

Реальный сложный пример

Если вас интересует более подробный пример из реальной жизни, загляните в репозиторий Github, который продемонстрирует следующее:

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

Пример репозитория GitHub



Дополнительные ресурсы

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



Особая благодарность

Я хочу воспользоваться моментом и поблагодарить всех, у кого мне удалось почерпнуть эту информацию. Я не придумал все это самостоятельно, но смог получить рабочее решение, объединив части из каждой из этих статей!







Кроме того, огромное спасибо читателю Medium ivanwonder и пользователю Github Милану Сарайе за указание на это и предоставление примера разрешения вилки.