【问题标题】:Angular 2: Inserting capture element dynamically when creating components (dynamically)Angular 2:在创建组件时动态插入捕获元素(动态)
【发布时间】:2017-01-31 04:04:44
【问题描述】:

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

这是代码

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. 试图用#captureElement 创建一个div 元素作为 以编程方式设置属性,但这不起作用。
  2. 尝试以编程方式创建一个随机元素并使用ViewContainerRef 找到它。那也不行。

【问题讨论】:

    标签: angular typescript angular2-template


    【解决方案1】:

    我们无法创建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) 之后通过调用上述appRef.attachView 注册其changeDetectorcomponentRef.hostView 扩展ChangeDetectorRef)(否则更改检测将不适用于您的组件)

    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

    另见

    【讨论】:

    • 非常感谢!这个HtmlContainer 班级将在哪里上课?我的意思是..哪个组件?
    • 我创建了HtmlContainer 作为辅助类,它运行良好。再次感谢。您能否详细说明HtmlContainer 班级在做什么?
    • 谢谢。如何维护动态组件以便删除它们?每次创建新的动态组件时,尝试维护一个对象,该对象在 addComponent 中存储对 componentRef.instance 的引用。对此调用.destroy() 并没有真正起作用。有没有更好的方法来做到这一点?
    • @yurzui 似乎HtmlContainer 中的dipose 方法没有处理组件。相反,该组件正是我们插入它的位置,有什么想法吗?
    猜你喜欢
    • 2020-11-11
    • 2017-07-07
    • 1970-01-01
    • 2016-09-26
    • 1970-01-01
    • 2018-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多