Angular 2:在创建组件时动态插入捕获元素(动态)

时间:2017-01-31 04:04:45

标签: angular typescript angular2-template

我的目标是创建子组件并插入父组件模板。有一些例子可以做到这一点。 但是,我在父组件中动态创建父组件模板(DOM元素),而大多数示例都显示静态创建带有capture元素的模板。

这是代码

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来查找它。这也不起作用。

1 个答案:

答案 0 :(得分:11)

我们无法创建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.hostViewChangeDetectorRef扩展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

另见