2017-01-31 4 views
4

Моя цель - создать дочерний компонент и вставить в шаблон родительского компонента. Есть примеры для этого. Тем не менее, я создаю шаблон родительского компонента (DOM Elements) динамически в родительском компоненте, тогда как большинство примеров, показанных статически, создают шаблон с элементом захвата.Угловая 2: динамическая установка элемента захвата при создании компонентов (динамически)

Вот код

app.component

import {Component, ViewChild, ViewContainerRef, ComponentFactoryResolver} from '@angular/core'; 
import {NewChildComponent} from "./newChild.component"; 

@Component({ 
selector: 'app-main', 
templateUrl: 'app.component.html' 
}) 
export class AppComponent { 

@ViewChild('captureElement', {read: ViewContainerRef}) 
captureElement: ViewContainerRef; 

constructor (private componentFactoryResolver: ComponentFactoryResolver) { 
var childComponent = this.componentFactoryResolver.resolveComponentFactory(NewChildComponent); 

var myArea = document.getElementById('myArea'); 
var myRow = document.createElement("div"); 
myArea.appendChild(myRow); 

//I want to add the capture element #myCapture as a child of myRow 
//Add something like this <div #captureElement></div> programmatically through JS/TS 
// How can I do this? 

// I then create the component 
    this.parent.createComponent(NewChildComponent); 

} 

app.component.html

<div id="myArea"> 
    <!-- Static way of doing it --> 
    <!--<div #captureElement></div>-->  
</div> 

Вместо статически определения в #captureElement, где будет вставлена ​​ребенок компонент, я хотел бы для его динамического создания в родительском компоненте и сделать его дочерним компонентом.

Вот список вопросов, которые я говорил прежде, чем я задал этот вопрос

Пробовал пару вещей

  1. Попробовал создать элемент div с #captureElement как атрибут программно, но это не сработает.
  2. Пытался создать случайный элемент программным путем и использовать ViewContainerRef, чтобы его найти. Это тоже не работает.

ответ

10

Мы не можем создать ViewContainerRef, так как ViewContainerRef существует только в пределах вида.

В версии 2.3.0 введено attachView, которое позволяет прикрепить обнаружение изменений к ApplicationRef.Вы можете создать некоторый класс, который инкапсулирует логику, как:

export class HtmlContainer { 
    private attached: boolean = false; 

    private disposeFn:() => void; 

    constructor(
    private hostElement: Element, 
    private appRef: ApplicationRef, 
    private componentFactoryResolver: ComponentFactoryResolver, 
    private injector: Injector) { 
    } 

    attach(component: Type<any>) : ComponentRef<any> { 
    if(this.attached) { 
     throw new Error('component has already been attached') 
    } 

    this.attached = true; 
    const childComponentFactory = this.componentFactoryResolver.resolveComponentFactory(component); 

    let componentRef = childComponentFactory.create(this.injector); 

    this.appRef.attachView(componentRef.hostView); 
    this.disposeFn =() => { 
     this.appRef.detachView(componentRef.hostView); 
     componentRef.destroy(); 
    }; 

    this.hostElement.appendChild((componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0]); 

    return componentRef; 
    } 

    dispose() { 
    if(this.attached) { 
     this.disposeFn(); 
    } 
    } 
} 

этот класс просто помощник, который

1) решает ваш динамический компонент

this.componentFactoryResolver.resolveComponentFactory(component); 

2) затем компилирует компонент по телефону compFactory.create

3) после этого регистрирует его changeDetector (componentRef.hostView распространяется ChangeDetectorRef) от cal лин упоминалось выше appRef.attachView (в противном случае изменить обнаружение не будет работать для вашего компонента)

4) и, наконец, добавляет RootNode вашего компонента к главному элементу

this.hostElement.appendChild((componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0]); 

Вы можете использовать его следующим образом:

@Component({ 
    selector: 'my-app', 
    template: `<div id="myArea"></div> `, 
    entryComponents: [NewChildComponent] 
}) 
export class AppComponent { 
    containers: HtmlContainer[] = []; 

    constructor(
    private appRef: ApplicationRef, 
    private componentFactoryResolver: ComponentFactoryResolver, 
    private injector: Injector) { 
    } 

    ngOnInit() { 
    var myArea = document.getElementById('myArea'); 
    var myRow = document.createElement("div"); 
    myArea.appendChild(myRow); 

    this.addComponentToRow(NewChildComponent, myRow, 'test1'); 
    this.addComponentToRow(NewChildComponent, myRow, 'test2'); 
    } 

    addComponentToRow(component: Type<any>, row: HTMLElement, param: string) { 
    let container = new HtmlContainer(row, this.appRef, this.componentFactoryResolver, this.injector); 
    let componentRef = container.attach(component); 
    componentRef.instance.param1 = param; 

    this.containers.push(container); 
    } 

    ngOnDestroy() { 
    this.containers.forEach(container => container.dispose()); 
    } 
} 

Plunker Example

Смотрите также

+0

Спасибо большое! Где этот класс «HtmlContainer» собирается сидеть? Я имею в виду, какой компонент? – am3

+0

Я создал «HtmlContainer» в качестве вспомогательных классов, и он отлично работал. Еще раз спасибо. Можно ли вам подробно рассказать о том, что делает класс «HtmlContainer»? – am3

+0

Спасибо. Как я могу поддерживать динамические компоненты, чтобы удалить их? Пробовал поддерживать объект, который хранил ссылку на 'componentRef.instance' в' addComponent' каждый раз, когда создавался новый динамический компонент. Вызов '.destroy()' на этом действительно не работает. Есть лучший способ сделать это? – am3