Angular2: программно создавать дочерние компоненты
Вопрос
Как создать дочерние компоненты внутри родительского компонента и затем отобразить их в представлении с помощью Angular2? Как убедиться, что инъекционные инъекции правильно введены в дочерние компоненты?
Пример
import {Component, View, bootstrap} from 'angular2/angular2';
import {ChildComponent} from './ChildComponent';
@Component({
selector: 'parent'
})
@View({
template: `
<div>
<h1>the children:</h1>
<!-- ??? three child views shall be inserted here ??? -->
</div>`,
directives: [ChildComponent]
})
class ParentComponent {
children: ChildComponent[];
constructor() {
// when creating the children, their constructors
// shall still be called with the injectables.
// E.g. constructor(childName:string, additionalInjectable:SomeInjectable)
children.push(new ChildComponent("Child A"));
children.push(new ChildComponent("Child B"));
children.push(new ChildComponent("Child C"));
// How to create the components correctly?
}
}
bootstrap(ParentComponent);
Изменить
Я нашел DynamicComponentLoader
в предпросмотре API docs. Но я получаю следующую ошибку, следуя примеру: Отсутствует директива динамического компонента в элементе 0
Ответы
Ответ 1
Это вообще не тот подход, который я бы принял. Вместо этого я буду полагаться на привязку данных к массиву, который будет отображать больше дочерних компонентов, поскольку объекты добавляются в массив подстановки. По существу дочерние компоненты, завернутые в ng-for
У меня есть пример, похожий на то, что он отображает динамический список дочерних элементов. Не 100% то же самое, но похоже, что концепция все та же:
http://www.syntaxsuccess.com/viewarticle/recursive-treeview-in-angular-2.0
Ответ 2
Предупреждение: DynamicComponentLoader устарел в RC.4
В Angular 2.0, loadIntoLocation
метод DynamicComponentLoader
служит для создания отношений родитель-потомок. Используя этот подход, вы можете динамически создавать отношения между двумя компонентами.
Вот пример кода, в котором бумага является моим родителем, а бюллетень - моим дочерним компонентом.
paper.component.ts
import {Component,DynamicComponentLoader,ElementRef,Inject,OnInit} from 'angular2/core';
import { BulletinComponent } from './bulletin.component';
@Component({
selector: 'paper',
templateUrl: 'app/views/paper.html'
}
})
export class PaperComponent {
constructor(private dynamicComponentLoader:DynamicComponentLoader, private elementRef: ElementRef) {
}
ngOnInit(){
this.dynamicComponentLoader.loadIntoLocation(BulletinComponent, this.elementRef,'child');
}
}
bulletin.component.ts
import {Component} from 'angular2/core';
@Component({
selector: 'bulletin',
template: '<div>Hi!</div>'
}
})
export class BulletinComponent {}
paper.html
<div>
<div #child></div>
</div>
В этом ответе упоминается несколько вещей, о которых вам нужно позаботиться
Ответ 3
Вы должны использовать ComponentFactoryResolver и ViewElementRef для добавления компонента во время выполнения. Посмотрите ниже код.
let factory = this.componentFactoryResolver.resolveComponentFactory(SpreadSheetComponent);
let res = this.viewContainerRef.createComponent(factory);
Поместите вышеуказанный код в свою функцию ngOnInit и замените "SpreadSheetComponent" на ваше имя компонента.
Надеюсь, что это сработает.
Ответ 4
Программно добавить компоненты в DOM в Angular 2/4 app
Нам нужно использовать метод жизненного цикла ngAfterContentInit()
из AfterContentInit
. Он вызывается после того, как содержимое директивы полностью инициализировано.
В parent-component.html
добавьте a div
следующим образом:
<div #container> </div>
Файл parent-component.ts
выглядит следующим образом:
class ParentComponent implements AfterContentInit {
@ViewChild("container", { read: ViewContainerRef }) divContainer
constructor(private componentFactoryResolver: ComponentFactoryResolver) { }
ngAfterContentInit() {
let childComponentFactory = this.componentFactoryResolver.resolveComponentFactory(childComponent);
this.divContainer.createComponent(childComponentFactory);
let childComponentRef = this.divContainer.createComponent(childComponentFactory);
childComponentRef.instance.someInputValue = "Assigned value";
}
}
Внутри src\app\app.module.ts
добавьте следующую запись в параметры метода @NgModule()
:
entryComponents:[
childComponent
],
Обратите внимание, что мы не обращаемся к div#container
с помощью подхода @ViewChild("container") divContainer
. Нам нужна эта ссылка вместо nativeElement
. Мы получим доступ к нему как ViewContainerRef
:
@ViewChild("container", {read: ViewContainerRef}) divContainer
ViewContainerRef
имеет метод под названием createComponent()
, который требует передачи компонента factory в качестве параметра. Для этого нам нужно ввести a ComponentFactoryResolver
. Он имеет метод, который в основном загружает компонент.
Ответ 5
Правильный подход зависит от ситуации, которую вы пытаетесь решить.
Если число детей неизвестно, то NgFor - правильный подход.
Если он исправлен, как вы упомянули, 3 ребенка, вы можете использовать DynamicComponentLoader
для их загрузки вручную.
Преимущества ручной загрузки - лучший контроль над элементами и ссылка на них внутри родителя (что также можно получить с помощью шаблонов...)
Если вам нужно заполнить данные детьми, это также можно сделать с помощью инъекции, родитель вводит объект данных, заполняющий детей на месте...
Опять же, много вариантов.
Я использовал "DynamicComponentLoader" в моем модальном примере, https://github.com/shlomiassaf/angular2-modal