【问题标题】:Angular 2- ContentChidren of parent component are undefined when dynamically creating child componentAngular 2-动态创建子组件时未定义父组件的ContentChidren
【发布时间】:2017-12-03 14:29:16
【问题描述】:

我一直在尝试制作一个标签视图,并认为内容投影可能是一个好方法。我是从这个article 学到的。我认为只需输入给定的组件数组就可以使其动态化,它们将显示为所选选项卡的页面。

这是我的尝试:

@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() 作为按钮的单击处理程序并不是为了避免此错误:"EXCEPTION: Expression has changed after it was checked"。如果我把它放在任何生命周期钩子中,我就会得到这个错误。虽然这是一个以后要处理的挑战。

所以基本上我在数组中创建每个组件,然后我将它们作为 ng-content 粘贴到每个创建的选项卡组件中。然后取出每个 Tab 组件并将其粘贴到 Tabs 组件的 ng-content 中。

问题是选项卡组件永远不会找到动态创建的选项卡子项的 contentChildren。这是未定义内容子项的选项卡组件的代码。

@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,谢谢!

【问题讨论】:

  • @ContentChildren 不适用于动态创建组件。这是设计使然
  • 哇,InitContent 我以前没见过那个!想想看,你可能会认出其中的一些代码,因为我想我是从以前的 plunk 中得到的 :)。
  • 这只是我初始化标签的自定义方法:)
  • 最后一件事是我有没有机会摆脱按钮并在 App.Component 加载时加载这个负载?我想我尝试了 ngOnInit,但在这样做之前出现了错误。也许自从你更新以来事情已经改变了......我会试试的。

标签: angular dynamic tabs transclusion angular2-ngcontent


【解决方案1】:

@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 工厂,因此 Angular 不会知道动态节点。

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

你可以在这个Plunkr

中观察

【讨论】:

    【解决方案2】:

    看看我在这里给出的例子dynamically add elements to DOM,也许你会在那里找到一些对你的问题有帮助的东西。此时在您的 plnkr 中,tabs 数组为空。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-07-21
      • 2017-02-24
      • 1970-01-01
      • 2017-07-07
      • 2020-08-06
      相关资源
      最近更新 更多