Динамическое создание компонентов в Angular с использованием ComponentFactoryResolver
— мощный механизм для рендеринга компонентов во время выполнения. Этот подход особенно полезен для:
Сначала создадим компонент, который будем рендерить динамически:
import { Component } from '@angular/core';
@Component({
selector: 'app-dynamic',
template: `<h2>Динамический компонент</h2>
<p>Создан в runtime!</p>`
})
export class DynamicComponent {}
В более старых версиях Angular нужно было добавлять динамический компонент в entryComponents
модуля:
@NgModule({
declarations: [DynamicComponent],
entryComponents: [DynamicComponent] // Требовалось до Angular 13
})
export class AppModule {}
В Angular 13+ это больше не требуется благодаря Ivy.
import { Component, ComponentFactoryResolver, ViewChild, ViewContainerRef, AfterViewInit } from '@angular/core';
import { DynamicComponent } from './dynamic.component';
@Component({
selector: 'app-host',
template: `<ng-container #container></ng-container>`
})
export class HostComponent implements AfterViewInit {
@ViewChild('container', { read: ViewContainerRef }) container: ViewContainerRef;
constructor(private resolver: ComponentFactoryResolver) {}
ngAfterViewInit() {
this.createDynamicComponent();
}
createDynamicComponent() {
// 1. Очищаем контейнер
this.container.clear();
// 2. Получаем фабрику компонента
const factory = this.resolver.resolveComponentFactory(DynamicComponent);
// 3. Создаем компонент
const componentRef = this.container.createComponent(factory);
// 4. Работаем с экземпляром компонента
componentRef.instance.someProperty = 'Значение';
// 5. Подписываемся на события
componentRef.instance.someEvent.subscribe(...);
}
}
ViewContainerRef
— это контейнер, куда будет вставлен компонент. Получаем его через @ViewChild
.
Сервис, который создает фабрику компонента по его типу.
Результат создания компонента, содержащий:
instance
— экземпляр компонентаhostView
— представление компонентаdestroy()
— метод для удаления компонента// Если DynamicComponent имеет @Input() inputData;
componentRef.instance.inputData = { key: 'value' };
// Если DynamicComponent имеет @Output() action = new EventEmitter();
componentRef.instance.action.subscribe(value => {
console.log('Получено значение:', value);
});
componentRef.destroy();
С введением Ivy API упростилось:
createDynamicComponent() {
this.container.clear();
const componentRef = this.container.createComponent(DynamicComponent);
// Работа с компонентом остается такой же
componentRef.instance.title = 'Динамический заголовок';
}
Управление памятью
Всегда вызывайте destroy()
для динамических компонентов при уничтожении родителя.
Change Detection
Динамические компоненты участвуют в проверке изменений. Для оптимизации можно использовать OnPush
.
Инжекция зависимостей
Динамические компоненты получают инжектор от родительского компонента.
Множественные компоненты
Можно создавать несколько экземпляров в одном контейнере:
const componentRef1 = this.container.createComponent(factory, 0);
const componentRef2 = this.container.createComponent(factory, 1);
Для простых случаев можно использовать *ngComponentOutlet
:
<ng-container *ngComponentOutlet="dynamicComponent"></ng-container>
ComponentFactoryResolver
(в старых версиях Angular) или прямое использование ViewContainerRef.createComponent()
(в Ivy) позволяют динамически создавать и управлять компонентами во время выполнения. Это мощный инструмент для сложных динамических интерфейсов, требующий внимательного управления жизненным циклом создаваемых компонентов.