2017-06-29 68 views
1

我一直在试图制作一个标签视图,并认为内容投影可能是一个好方法。我从这个article中了解到它。我认为只要输入一个给定的组件数组就可以使其动态化,并且它们将显示为选定选项卡的页面。当动态创建子组件时,父组件的Angular 2- ContentChidren是未定义的

这是我在做这样的尝试:

@Component({ 
    selector: 'my-app', 
    template:` 
    <h1>Experiments</h1> 
    <button class="add-button" (click)="add()">Add</button>` 
}) 
export class App { 

    components:Array<any> = [PrepSetupTab,FinalizeTab] 

    constructor(private cdr: ChangeDetectorRef, 
       private compFR: ComponentFactoryResolver, 
       private viewContainer: ViewContainerRef){} 

    add():void{ 
     var transTabRefs: Array<any>= [] 

     this.components.forEach(tabComponent=>{ 

      let compFactory = this.compFR.resolveComponentFactory(tabComponent); 
      let compRef = this.viewContainer.createComponent(compFactory); 
      let tabFactory = this.compFR.resolveComponentFactory(Tab); 

      let transcludedTabRef = this.viewContainer.createComponent(tabFactory,this.viewContainer.length - 1, undefined, [[compRef.location.nativeElement]]); 
      transTabRefs.push(transcludedTabRef.location.nativeElement); 
     }) 

     let tabsFactory = this.compFR.resolveComponentFactory(Tabs); // notice this is the tabs not tab 
     this.viewContainer.createComponent(tabsFactory,0,undefined,[transTabRefs]); 

    } 


} 

侧面说明,添加()作为按钮的点击处理程序不打算等功能,以避免这个错误:"EXCEPTION: Expression has changed after it was checked"。如果我把它放在任何生命周期钩子中,我会得到这个错误。虽然这对于稍后处理是一个挑战。

所以基本上我创建了数组中的每个组件,然后我将它们粘贴为每个创建的Tab组件的ng内容。然后将每个Tab组件粘贴到ng内容中以供制表符组件使用。

问题是Tabs组件未找到动态创建的Tab儿童的contentChildren。以下是未定义内容子项的Tabs组件的代码。

@Component({ 
    selector: 'tabs', 
    template:` 
    <ul class="nav nav-tabs"> 
     <li *ngFor="let tab of tabs" (click)="selectTab(tab)" [class.active]="tab.active"> 
     <a>{{tab.title}}</a> 
     </li> 
    </ul> 
    <ng-content></ng-content> 
    ` 
}) 
export class Tabs implements AfterContentInit { 

    @ContentChildren(Tab) tabs: QueryList<Tab>; 

    // contentChildren are set 
    ngAfterContentInit() { 
    // get all active tabs 
    let activeTabs = this.tabs.filter((tab)=>tab.active); 

    // if there is no active tab set, activate the first 
    if(activeTabs.length === 0) { 
     this.selectTab(this.tabs.first); 
    } 
    } 

    selectTab(tab: Tab){ 
    // deactivate all tabs 
    this.tabs.toArray().forEach(tab => tab.active = false); 

    // activate the tab the user has clicked on. 
    tab.active = true; 
    } 

} 

这似乎是很清楚,我认为该卡组件,在不同的时间产生了以后,当我需要访问它们从标签成分含量的孩子。我也尝试使用ChangeDetectionRef.changeDetect(),但这没有帮助。

也许通过内容投影完成这一切并不是最简单的方式或最好的,所以我愿意提供建议。这是plunk,谢谢!

+0

'@ ContentChildren'不会与动态创建组件工作。这是由设计 – yurzui

+0

http://plnkr.co/edit/t1GnzDDR2s5g8PSDojXv?p=preview – yurzui

+0

哇,InitContent我以前没有看到过!想想看,你可能会认识到这些代码中的一些,因为我认为我从之前的plu​​nk中获得了它:)。 –

回答

0

@ContentChildren不适用于动态创建的组件。

为什么?

这是因为投影的候选人必须在编译时知道。

下面是用于计算ContentChildren

function calcQueryValues(view, startIndex, endIndex, queryDef, values) { 
    for (var /** @type {?} */ i = startIndex; i <= endIndex; i++) { 
     var /** @type {?} */ nodeDef = view.def.nodes[i]; 
     var /** @type {?} */ valueType = nodeDef.matchedQueries[queryDef.id]; 
     if (valueType != null) { 
      values.push(getQueryValue(view, nodeDef, valueType)); 
     } 

它使用了在viewDefinition对于组分声明节点的功能。 当您通过createComponent创建tabs组件并手动传递可投影节点时,您没有更改viewDefinition工厂的tabs组件,因此angular将不会意识到动态节点。

一个可能的解决办法是手动初始化你的标签

你可以看到它在这个Plunkr

相关问题