角度获取动态创建的组件的viewContainerRef

时间:2018-06-21 19:11:59

标签: angular dynamic components

在组件中,我确实使用服务来加载JSON数据。依靠这些具有树结构的数据,我想动态地创建新的组件:

import { Component, AfterViewInit, OnDestroy, ComponentFactoryResolver, ViewContainerRef, Input, ViewChild } from '@angular/core';

import { MenuComponent } from '../menu/menu.component';
import { RowComponent } from '../row/row.component';

import { DynamicComponentInterface } from '../../interfaces/dynamicComponentInterface';
import { SlotDirective } from './slot.directive';

import { ContentService } from '../../services/content.service';
import { DynamicComponentsHostService } from '../../services/dynamicComponentsHost.service';



@Component({
  selector: 'app-slot',
  templateUrl: './slot.component.html',
  styleUrls: ['./slot.component.css']
})
export class SlotComponent implements AfterViewInit, OnDestroy  {
  @Input() id:number;
  @ViewChild(SlotDirective) slot: SlotDirective;
  private slotDataTree;
  private componentRef;
  private contentReadySubscription;

  constructor(
    private contentService : ContentService,
    private componentFactoryResolver : ComponentFactoryResolver,
    private viewContainerRef: ViewContainerRef,
    private dynamicComponentsHostService: DynamicComponentsHostService

  ) {

  }

  ngAfterViewInit(){
    // get JSON data with TreeStructure from Service
    if(this.contentService.stateReady){
      this.slotDataTree = this.contentService.getCurrentBySlotId(this.id);
      this.renderContentTree();
    }
    else{
      this.contentReadySubscription = this.contentService.getCurrentPageContentReadyObserver().subscribe(stateReady => {
    if(stateReady ){
      this.slotDataTree = this.contentService.getCurrentBySlotId(this.id);
      this.renderContentTree();
    }
      });
    }
  }

  renderContentTree(){
    if(this.slotDataTree){


      let componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.dynamicComponentsHostService.getDynamicComponentType(this.slotDataTree.type));
      let viewContainerRef = this.slot.viewContainerRef;
      let componentRef = viewContainerRef.createComponent(componentFactory);


      (<DynamicComponentInterface>componentRef.instance).data = this.slotDataTree;

    // Works like a charm.
    // now I want to create children for the newly created component
    // so I need the viewContainerRef of componentRef



    // this delivers a viewRef
      console.log(componentRef.hostView);

    // this delivers sometimes a viewContainerRef in console, but a comile error that _viewRef does not exists

      console.log(componentRef._viewRef._viewContainerRef);

    // I cannot do this in the dynamicly rendered components class its self, because of circular dependencies that would occour then
      /*
      if(this.slotDataTree.children.length){
    for(var i = 0; i < this.slotDataTree.children.length; i++){

      let componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.dynamicComponentsHostService.getDynamicComponentType(this.slotDataTree.children[i].type));
      let viewContainerRef = componentRef._viewRef._viewContainerRef;
      let componentRef = viewContainerRef.createComponent(componentFactory);

    }
      }
      */
      if(this.contentReadySubscription){
    this.contentReadySubscription.unsubscribe();
      }
    }
  }

  ngOnDestroy() {
    if(this.contentReadySubscription){
      this.contentReadySubscription.unsubscribe();
    }
  }
}

我可以动态创建组件。但是我也要为创建的组件创建子组件。意味着我需要一个我刚刚创建的组件的viewRefContainer。

由于循环依赖,我无法在动态创建的组件类中动态创建组件。

我该如何解决?

谢谢

1 个答案:

答案 0 :(得分:1)

这就是我现在解决的方式。首先,我使用一个根组件,该根组件是动态创建的组件树的起源。也许服务会更好:

import { Component, AfterViewInit, OnDestroy, OnInit, ComponentFactoryResolver, Input, ViewChild, ChangeDetectorRef } from '@angular/core';
import { MenuComponent } from '../menu/menu.component';
import { DynamicComponent } from '../dynamic/dynamic.component';
import { SlotDirective } from '../../directives/slot.directive';
import { ContentService } from '../../services/content.service';
import { EditService } from '../../services/edit.service';
import { DynamicComponentsHostService } from '../../services/dynamicComponentsHost.service';
import { DynamicComponentsData } from '../../classes/dynamicComponentsData';

@Component({
  selector: 'app-slot',
  templateUrl: './slot.component.html',
  styleUrls: ['./slot.component.css']
})
export class SlotComponent implements OnDestroy, OnInit  {
  @Input() id:number;
  @ViewChild(SlotDirective) slot: SlotDirective;
  private componentRef;
  private contentReadySubscription;
  public slotDataTree : DynamicComponentsData = new DynamicComponentsData({},false);

  constructor(
    protected contentService : ContentService,
    private editService : EditService,
    private componentFactoryResolver : ComponentFactoryResolver,
    private dynamicComponentsHostService: DynamicComponentsHostService
  ) {
  }

  ngOnInit(){
    var slotData;
    if(this.contentService.stateReady){
      if( slotData = this.contentService.getCurrentBySlotId(this.id) ){
        this.slotDataTree = slotData;
      }
      else{
        this.slotDataTree.type = 'none';
      }
      this.renderContentTree();
    }

    this.contentReadySubscription = this.contentService.getCurrentPageContentReadyObserver().subscribe(stateReady => {
      if(stateReady ){
        if( slotData = this.contentService.getCurrentBySlotId(this.id) ){
          this.slotDataTree = slotData;
        }
        else{
          this.slotDataTree.type = slotData;
        }
        this.renderContentTree();
      }
    });

    DynamicComponentsData.changed.subscribe( changeObject =>{
      this.renderContentTree();
    });
  }

  renderChilds(viewContainerRef,childs){
    for(var i = 0; i < childs.length; i++){
      // create the dynamic component. Use a service to get the types
      let componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.dynamicComponentsHostService.getDynamicComponentType(childs[i].type).class);
      let componentRef = viewContainerRef.createComponent(componentFactory);
      // give some data
      (<DynamicComponent>componentRef.instance).data = childs[i];
      // subscribe an event emitter
      (<DynamicComponent>componentRef.instance).sendViewContainerRef.subscribe(
        (event:any)=>{
          if(event.childs){
            this.renderChilds(event.viewContainerRef, event.childs);
          }
        }
      );
    }
  }

  renderContentTree(){
    let viewContainerRef = this.slot.viewContainerRef;
    viewContainerRef.clear();

    if(this.contentService.getCurrentBySlotId(this.id)){
      this.renderChilds(this.slot.viewContainerRef, this.contentService.getCurrentBySlotId(this.id).children);
      if(this.contentReadySubscription){
        this.contentReadySubscription.unsubscribe();
      }
    }
  }

  ngOnDestroy() {
    if(this.contentReadySubscription){
      this.contentReadySubscription.unsubscribe();
    }
  }
}