Angular 2: Inserting capture element dynamically when creating components (dynamically)

后端 未结 1 2002
鱼传尺愫
鱼传尺愫 2020-12-18 09:06

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

相关标签:
1条回答
  • 2020-12-18 09:33

    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

    • Angular2 - Component into dynamicaly created element
    • Angular2 Dynamic Component Injection in Root
    • https://github.com/angular/material2/blob/2.0.0-beta.1/src/lib/core/portal/dom-portal-host.ts#L30-L86 (you can find here fallback for angular < 2.3.0)
    0 讨论(0)
提交回复
热议问题