Как Angular разрешает динамические компоненты

Как Angular разрешает динамические компоненты

От автора: динамические Angular компоненты – одна из основных концепций, представленных в Angular, они позволяют разработчикам динамически отображать представление компонента во время выполнения. В этой статье объясняется, как класс ComponentFactoryResolver разрешает ComponentFactory. Зачем это знать? Знание того, как работают основные части Angular, поможет лучше понять его, и вы будете увереннее при создании приложений и решении проблем, связанных с ошибками.

Angular Components и Factories

При исполнении Angular не состоит из компонентов. Перед выполнением все скомпилировано в JavaScript. Компоненты, модули, директивы все превращаются в фабрики. Другими словами, Angular состоит из фабрик (ComponentFactory, NgModuleFactory). Поэтому, прежде чем начать выполнение, выполните следующие действия:

Будет скомпилировано в следующее:

В этой статье мы подробно рассмотрим, как Angular разрешает фабрику динамического компонента, используя метод resolveComponentFactory:

Пример

Допустим, у нас есть ChildComponent:

И ParentComponent, который динамически добавляет представление ChildComponent:

Круто! Элемент template – сюда будет добавлен ChildComponent.

Наиболее важные вещи, которые мы должны здесь отметить, это инъекция ComponentFactoryResolver и разрешение ChildComponent в методе createChild. Когда нажимается кнопка «Create Child», запускается метод createChild, который сначала очищает элемент template, а затем получает фабрику компонентов ChildComponent и создает представление ChildComponent в DOM.

Можно задать следующий вопрос: Что такое ComponentFactoryResolver? И как его метод resolveComponentFactory получает фабрику компонента?

Что такое ComponentFactoryResolver?

ComponentFactoryResolver — это класс в Angular, который хранит фабрики компонентов в хранилище key-value.

Он принимает массив типа ComponentFactory, родительский ComponentFactoryResolver и NgModuleRef качестве аргументов для создания экземпляра.

Во время создания объекта массив factories перебирается в цикле и сохраняет каждый компонент ComponentFactory в Map с типом componentType в качестве ключа и фабрикой в качестве значения:

Это больше похоже на объект. Он использует механизм key-value для хранения и извлечения значений:

Как resolveComponentFactory получает фабрику компонента?

Просто, ведь он хранит ее в Map с парой ключ-значение. Все, что он должен сделать, чтобы получить фабрику компонента — это сослаться на нее по ее ключу.

Метод get используется в Map для получения значения по его ключу. Итак, здесь метод get используется для извлечения ComponentFactory по его ключу (component). Следующий вопрос: как ComponentFactoryResolver получает массив ComponentFactory?

Ранее в статье мы узнали, что компоненты перед выполнением компилируются до кода JS или factories. Компоненты компилируются для генерации ComponentFactory при выполнении, а модули генерируют NgModuleFactory.

Функция createNgModuleFactory используется для генерации NgModuleFactory. AppModule нашего приложения:

Его фабрика будет выглядеть так:

Обратите внимание на неясные имена методов, начинающиеся с символа тета ɵ. Так Angular уменьшает размер пакета при минимизации. Этот метод ссылается на реализацию фактического метода, ɵcmf вызывает createNgModuleFactory , ɵmpd => moduleProviderDef , ɵmod => moduleDef.

Мы видим, что createNgModuleFactory принимает в качестве аргументов класс Module, компоненты начальной загрузки и фабрику определения модуля.

Нас больше интересует фабрика определения модуля. NgModuleFactroyDefinition предоставляет контекст, используемый для разрешения компонентов и инъекции зависимостей. Заглянув в него, мы видим, что он вызывает функцию moduleDef, которая передается массивом вызовов moduleProviderDef. moduleProviderDef — это функция, которая определяет, как класс будет храниться и извлекаться через Injector.

Здесь параметр flags указывает, как должно быть разрешено или инстанцировано значение класса/провайдера:

Параметр token: any является ключом, по которому значение класса / провайдера извлекается через Injector. value — фактическое значение token. deps: ([DepFlags, any] | any)[] — это зависимости, используемые для построения экземпляра класса.

moduleProviderDef парсит все это в объект и возвращает его. moduleDef получает массив из него и парсит его в массив providers.

Когда фабрика модулей создается посредством вызова функции createNgModuleFactory, выполняется серия вызовов, которые в конечном итоге приводят к разрешению провайдеров:

Здесь массив провайдеров, сгенерированный функцией moduleDef, перебирается в цикле, и экземпляр каждого провайдера создается и сохраняется в _providers объекта NgModuleDef_:

Мы рассмотрели все это, чтобы узнать, как ComponentFactoryResolver получает свой массив ComponentFactory. Теперь давайте посмотрим на наше определение фабрики модулей.

Если посмотреть настройки ComponenetFactoryResolver, мы видим, что здесь передается 512, который обозначает TypeClassProvider в объекте NodeFlags. Это говорит Angular о том, что мы настраиваем провайдер из класса.

Далее i0.ComponentFactoryResolver передается как токен, а i0.ɵCodegenComponentFactoryResolver — как значение. Последний параметр — это зависимости ComponentFactoryResolver.

Помните, когда мы рассмотрели определение ComponentFactoryResolver, мы узнали, что он принимает в свой конструктор в качестве параметров factories: ComponentFactory, private _parent: ComponentFactoryResolver, private _ngModule: NgModuleRef.

Итак, экземпляр ComponentFactoryResolver будет создан следующим образом:

Как видно, передается массив ComponentFactory. Таким образом, во время создания объекта происходит перебор массива и сохранение пары ключ-значение каждого компонента ComponentFactory в _factories Map. Поэтому, когда мы делаем это:

ComponentFactoryResolver перебирает в цикле _factories Map и извлекает ComponentFactory из ключа поставляемого компонента ChildComponent.

Если мы так запустим наше приложение и нажмем кнопку Create Child, будет вызвана ошибка noComponentFactoryError.

Почему? Поскольку ChildComponent не был передан в ComponentFactoryResolver в параметре массива ComponentFactory, он не присутствовал в _factories ключей значений _factories.

Вот почему в уроках, посвященных динамическим компонентам Angular, всегда ссылаются на добавление всех динамически созданных компонентов в свойство entryComponents вашего корневого NgModule (AppModule).

Итак, Angular Compiler «знает», они должны быть добавлены к зависимостям ComponentResolverFactory во время компиляции.

Теперь, когда настроены зависимости ComponentFactoryResolver, ChildComponentFactory будет присутствовать:

Заключение

Мы узнали, как Angular разрешает динамические компоненты. Функция moduleProviderDef используется для настройки провайдера для фреймворка вставки зависимостей Angular.

Именно здесь ComponentFactory динамических компонентов передается классу ComponentFactoryResolver для разрешения фабрики компонента во время выполнения.

Вот и все, теперь мы знаем, что происходит под капотом, когда мы используем метод resolveComponentFactory. Спасибо!!!

Автор: Chidume Nnamdi

Источник: //blog.bitsrc.io/

Редакция: Команда webformyself.

Метки:

Похожие статьи:

Комментарии Вконтакте: