Angular 2-动态创建子组件时父组件的ContentChidren未定义
Angular 2- ContentChidren of parent component are undefined when dynamically creating child component
我一直在尝试制作一个选项卡视图,并认为内容投影可能是一个很好的选择 approach.I 从这个 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"
。如果我把它放在任何生命周期中,我都会得到这个错误 hooks.Although 不过,这是一个以后要处理的挑战。
所以基本上我是在数组中创建每个组件,然后我将它们作为 ng-content 粘贴到每个创建的选项卡组件中。然后获取每个 Tab 组件并将其粘贴到 Tabs 组件的 ng-content 中。
问题是 Tabs 组件永远找不到动态创建的 Tab 子项的 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;
}
}
我很清楚 Tab 组件的创建时间与我需要从 Tabs 组件作为内容子项访问它们的时间不同。我也尝试过使用 ChangeDetectionRef.changeDetect() 但这没有帮助。
也许通过内容投影来完成这一切并不是最简单或最好的方法,所以我愿意接受建议。这是plunk,谢谢!
看看我在此处给出的示例 ,也许您会发现一些对您的问题有帮助的东西。此时在你的 plnkr 中 tabs 数组是空的。
@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
中观察
我一直在尝试制作一个选项卡视图,并认为内容投影可能是一个很好的选择 approach.I 从这个 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"
。如果我把它放在任何生命周期中,我都会得到这个错误 hooks.Although 不过,这是一个以后要处理的挑战。
所以基本上我是在数组中创建每个组件,然后我将它们作为 ng-content 粘贴到每个创建的选项卡组件中。然后获取每个 Tab 组件并将其粘贴到 Tabs 组件的 ng-content 中。
问题是 Tabs 组件永远找不到动态创建的 Tab 子项的 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;
}
}
我很清楚 Tab 组件的创建时间与我需要从 Tabs 组件作为内容子项访问它们的时间不同。我也尝试过使用 ChangeDetectionRef.changeDetect() 但这没有帮助。
也许通过内容投影来完成这一切并不是最简单或最好的方法,所以我愿意接受建议。这是plunk,谢谢!
看看我在此处给出的示例
@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
中观察