Angular获取动态创建的组件的viewContainerRef
Posted
技术标签:
【中文标题】Angular获取动态创建的组件的viewContainerRef【英文标题】:Angular get viewContainerRef of dynamicly created component 【发布时间】:2018-12-01 05:27:18 【问题描述】:在一个组件中,我确实使用服务来加载 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。
由于循环依赖,我无法在习惯于动态创建的组件类中动态创建组件。
我该如何解决这个问题?
谢谢
【问题讨论】:
你有没有解决这个问题?我现在也有同样的需求 是的,现在我使用了一个服务,它承载了所有可能必须创建的组件类型。然后我使用创建第一个动态组件的根组件。这个根组件订阅了动态创建的组件的观察者。动态创建的组件的观察者将其 viewRefContainer 发送回根组件。根组件使用 viewRefContainer 来创建 childComponents 等等。 感谢您的回答!我得到了它的工作:) 【参考方案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();
【讨论】:
以上是关于Angular获取动态创建的组件的viewContainerRef的主要内容,如果未能解决你的问题,请参考以下文章