Создание динамического компанента в Angular

Jul 28, 2019

В этой статье мы узнаем, как динамически создавать компоненты в Angular.

Для начала нам нужен компонент:

@Component({
    selector: "alert",
    template: `<h1>Alert {{type}}</h1>`
})
export class AlertComponent {
    @Input() type: string = "success";
}

Для простоты мы будем использовать простой компонент alert, который принимает Input свойство type (для определения типа алерта).

Далее, если вы подумаете об этом, в конечном итоге, компоненты являются элементами DOM, поэтому, когда вам нужно добавить элемент, вам нужно место, куда вы можете «поместить» его.

@Component({
    selector: 'my-app',
    template: `<template #alertContainer></template>`
})
export class App {}

В Angular это место называется контейнером.

В компоненте my-app мы создаем элемент template. Мы также используем хеш-символ (#) для объявления ссылочной переменной с именем alertContainer. Элемент template в Angular это контейнер.

Примечание. Контейнером может быть любой элемент или компонент DOM.

Теперь нам нужно получить ссылку на наш элемент template в компоненте my-app.

@Component({
    selector: 'my-app',
    template: `<template #alertContainer></template>`
})
export class App {
    @ViewChild("alertContainer", {static: false, read: ViewContainerRef }) container;
}

Мы можем получить ссылку на элемент template с помощью декоратора ViewChild, который также принимает в качестве параметра локальную переменную.

Возвращаемым значением по умолчанию декоратора ViewChild является экземпляр компонента или элемент DOM, но в нашем случае нам нужно получить элемент вида ViewContainerRef.

Как следует из названия, ViewContainerRef является ссылкой на контейнер. ViewContainerRef хранит ссылку на элемент template (наш контейнер), а также предоставляет API для создания компонентов.

Давайте добавим две кнопки, которые помогут нам создать компонент alert.

@Component({
    selector: 'my-app',
    template: `
        <template #alertContainer></template>
        <button (click)="createComponent('success')">Create success alert</button>
        <button (click)="createComponent('danger')">Create danger alert</button>
        `
    })
export class App {
    @ViewChild("alertContainer", { read: ViewContainerRef }) container;
}

Прежде чем мы перейдем к методу createComponent(), нам нужно добавить еще один сервис.

export class App {
    @ViewChild("alertContainer", { read: ViewContainerRef }) container;

    constructor(private resolver: ComponentFactoryResolver) {}

}

Служба ComponentFactoryResolver предоставляет один основной метод - resolComponentFactory.

Метод resolComponentFactory() принимает компонент и возвращает ComponentFactory.

Вы можете представить ComponentFactory как объект, который знает, как создать компонент.

Как вы можете видеть, ComponentFactory предоставляет метод create().

Для последнего шага.

createComponent(type) {
    this.container.clear();
    const factory: ComponentFactory = this.componentFactoryResolver.resolveComponentFactory(AlertComponent);

    this.componentRef = this.container.createComponent(factory);

    this.componentRef.instance.type = type;

    this.componentRef.instance.output.subscribe(event => console.log(event));

}

Давайте объясним, что происходит по частям.

this.container.clear();

Каждый раз, когда нам нужно создать компонент, нам нужно удалить предыдущее представление, иначе будет добавлено больше, чем требуется компонентов в контейнер. (не требуется, если вам нужно несколько компонентов)

const factory: ComponentFactory = this.resolver.resolveComponentFactory(AlertComponent);

Метод resolComponentFactory() берет компонент и возвращает рецепт того, как создать компонент.

this.componentRef: ComponentRef = this.container.createComponent(factory);

Мы вызываем метод createComponent() с рецептом. Внутри этот метод вызовет метод create() из фабрики и добавит компонент как родственный элемент в наш контейнер.

Теперь у нас есть ссылка на наш новый компонент, и мы можем установить наше входящее Input свойство type.

this.componentRef.instance.type = type;

Вы также можете подписаться на Output компонента:

this.componentRef.instance.output.subscribe(event => console.log(event));

И не забудьте уничтожить компонент:

ngOnDestroy() {
    this.componentRef.destroy();
}

Последний шаг - добавить ваши динамические компоненты в раздел entryComponents:

@NgModule({
    entryComponents: [ AlertComponent ],
    bootstrap: [ App ]
})
export class AppModule {}

demo на stackblitz

источник

Добавить комментарий
Комментарии:
роджик
Sep 4, 2019
КомпАнент? ты серьезно? сколько тебе лет?