Angular 2:创建组件时动态插入捕获元素(动态) [英] Angular 2: Inserting capture element dynamically when creating components (dynamically)

查看:22
本文介绍了Angular 2:创建组件时动态插入捕获元素(动态)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

这是代码

应用程序组件

import {Component, ViewChild, ViewContainerRef, ComponentFactoryResolver} from '@angular/core';从./newChild.component"导入{NewChildComponent};@成分({选择器:'app-main',templateUrl: 'app.component.html'})导出类 AppComponent {@ViewChild('captureElement', {读取:ViewContainerRef})捕获元素:ViewContainerRef;构造函数(私有 componentFactoryResolver:ComponentFactoryResolver){var childComponent = this.componentFactoryResolver.resolveComponentFactory(NewChildComponent);var myArea = document.getElementById('myArea');var myRow = document.createElement("div");myArea.appendChild(myRow);//我想添加捕获元素#myCapture作为myRow的子元素//添加类似这样的

通过 JS/TS 以编程方式//我怎样才能做到这一点?//然后我创建组件this.parent.createComponent(NewChildComponent);}

app.component.html

<!-- 静态方法--><!--<div #captureElement></div>-->

我不想在 #captureElement 中静态定义插入子组件的位置,而是想在父组件中动态创建它并使其成为子组件.

这是我在问这个问题之前提到的问题列表

尝试了一些东西

  1. 尝试创建一个 div 元素,并将 #captureElement 作为以编程方式属性,但这不起作用.
  2. 尝试以编程方式创建一个随机元素并使用 ViewContainerRef 找到它.这也行不通.

解决方案

我们不能创建 ViewContainerRef,因为 ViewContainerRef 只存在于视图中.>

在 2.3.0 中,引入了 attachView,它允许您将更改检测附加到 ApplicationRef.您可以创建一些类来封装您的逻辑,例如:

导出类 HtmlContainer {私人附加:boolean = false;私有 disposeFn: () =>空白;构造函数(私有主机元素:元素,私有 appRef: ApplicationRef,私有 componentFactoryResolver: ComponentFactoryResolver,私人注射器:注射器){}attach(component: Type) : ComponentRef{如果(这个.附加){throw new Error('组件已经被附加')}this.attached = true;const childComponentFactory = this.componentFactoryResolver.resolveComponentFactory(component);让 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).rootNodes[0]);返回组件引用;}处置(){如果(这个.附加){this.disposeFn();}}}

这个类只是帮手

1) 解析您的动态组件

this.componentFactoryResolver.resolveComponentFactory(component);

2) 然后通过调用 compFactory.create

来编译组件

3) 然后通过调用上面提到的 appRef.attachView 注册它的 changeDetector (componentRef.hostView extends ChangeDetectorRef)代码>(否则更改检测将不适用于您的组件)

4) 最后将组件的 rootNode 附加到宿主元素

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

您可以按如下方式使用它:

@Component({选择器:'我的应用',模板:`<div id="myArea"></div>`,entryComponents: [NewChildComponent]})导出类 AppComponent {容器:HtmlContainer[] = [];构造函数(私有 appRef: ApplicationRef,私有 componentFactoryResolver: ComponentFactoryResolver,私人注射器:注射器){}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, row: HTMLElement, param: string) {let container = new HtmlContainer(row, this.appRef, this.componentFactoryResolver, this.injector);让 componentRef = container.attach(component);componentRef.instance.param1 = param;this.containers.push(container);}ngOnDestroy() {this.containers.forEach(container => container.dispose());}}

Plunker 示例

另见

My goal is to create a child component and insert into the parent component template. There are examples to do this. However, I create parent component template (DOM Elements) dynamically in the parent component while most of the examples shown statically create the template with the capture element.

Here's the code

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>

Instead of statically defining in #captureElement where the child component would be inserted, I would like to create it dynamically in the parent component and make it a child component.

Here are a list of questions I referred before I asked this question

Tried a couple of things

  1. Tried to create a div element with a #captureElement as an attribute programmatically but that doesn't work.
  2. Tried to create a random element programmatically and use ViewContainerRef to find it. That doesn't work either.

解决方案

We can't create a ViewContainerRef, as a ViewContainerRef only exists within a view.

In 2.3.0, attachView was introduced which allows you to be able to attach change detection to the ApplicationRef. You can create some class that will encapsulate your logic like:

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();
    }
  }
}

this class is just helper that

1) resolves your dynamic component

this.componentFactoryResolver.resolveComponentFactory(component);

2) then compiles component by calling compFactory.create

3) after that registers its changeDetector (componentRef.hostView extends ChangeDetectorRef) by calling mentioned above appRef.attachView (otherwise change detection won't work for your component)

4) and finally appends the rootNode of your component to the host element

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

You can use it as follows:

@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

See also

这篇关于Angular 2:创建组件时动态插入捕获元素(动态)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆