动态创建子组件时未定义父组件的Angular 2- ContentChidren [英] Angular 2- ContentChidren of parent component are undefined when dynamically creating child component

查看:111
本文介绍了动态创建子组件时未定义父组件的Angular 2- ContentChidren的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在尝试制作选项卡视图,并认为内容投影可能是一种不错的方法.我从

I've been trying to make a tab view and thought content projection might be a good approach.I learned it from this article. I thought that I could make it dynamic by just inputting a given array of components, and they would be displayed as the page for the selected tab.

这是我尝试执行的操作:

Here is my attempt at doing this:

@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]);

  }


}

旁注,add()作为按钮的单击处理程序不是避免该bug的预期功能:"EXCEPTION: Expression has changed after it was checked".如果将其放在任何生命周期挂钩中,都会出现此错误.尽管这是以后要解决的挑战.

Side note, add() as a click handler of button isn't intended functionality other to avoid this bug: "EXCEPTION: Expression has changed after it was checked". I get this bug if I put it in any life cycle hooks.Although that's a challenge to deal with later though.

所以基本上我是在数组中创建每个组件,然后将它们作为每个创建的Tab组件的ng-content粘贴进去.然后获取每个Tab组件并将其粘贴在Tabs组件的ng-content中.

So basically I am creating the each Component in the array, and then i'm sticking them in as ng-content for each created Tab Component. Then taking each Tab component and sticking it in ng-content for Tabs Component.

问题是Tabs组件永远找不到动态创建的Tab子项的contentChildren.这是未定义内容子项的Tabs组件的代码.

The problem is Tabs Component doesn't ever find the contentChildren of the dynamically created Tab children. Here is the code for the Tabs Component where the the content children are undefined.

@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;
  }

}

对我来说,很显然,Tab组件是在与我需要从Tabs组件作为内容子项访问它们的时间不同的时间创建的.我也尝试过使用ChangeDetectionRef.changeDetect(),但这没有帮助.

It seems to be pretty clear to me that the Tab Component is created at a different time then when I need to access them as content children from the Tabs Component. I've also tried using the ChangeDetectionRef.changeDetect() but that doesn't help.

也许通过内容投射来做到这一切都不是最简单或最好的方法,所以我愿意提出建议.这是 plunk ,谢谢!

Maybe doing this all through content projection isn't easiest way or best so I am open to suggestions. Here is the plunk, thanks!

推荐答案

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

@ContentChildren won't work for components created dynamically.

为什么?

那是因为必须在编译时知道投影的候选对象.

That's because candidates for projection must be known at compile time.

这是用于计算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组件并手动传递可投影节点时,您没有更改Tabs组件的viewDefinition工厂,因此angular不会意识到动态节点.

It uses nodes that are declared in viewDefinition for component. When you created tabs component via createComponent and passed projectable nodes manually you didn't change viewDefinition factory for tabs component and hence angular won't be aware about dynamic nodes.

可能的解决方案可能是手动初始化标签页

A possible solution could be manually initializing your tabs

您可以在此 Plunkr

You can observe it in this Plunkr

这篇关于动态创建子组件时未定义父组件的Angular 2- ContentChidren的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆