以下是main.html
的模板:
<button (click)="displayAndUseMyCustomComp()"></button>
<my-custom-comp *ngIf="isMyCustomCompDisplayed" #myCustomComp="myCustomComp"></my-custom-comp>
和main.component.ts
:
export class MainComponent {
constructor() {}
private isMyCustomCompDisplayed boolean = false
@ViewChild('myCustomComp') myCustomComp: MyCustomComp
displayAndUseMyCustomComp() {
this.isMyCustomCompDisplayed = true
console.log(this.myCustomComp) // prints undefined
setTimeout(() => {
console.log(this.myCustomComp) // prints {object}
})
}
}
我发生的事情是,在我将isMyCustomCompDisplayed
设置为true
后,我的模板尚未刷新。但是,如果我使用setTimeout
,myCustomComp
会更新,我的问题就会消失。这是中等的hacky,我想知道我正在做的事情的正确方法是什么。
答案 0 :(得分:7)
displayAndUseMyCustomComp
角度更新ViewChild
查询列表,作为更改检测的一部分。当Angular运行初始更改检测时,isMyCustomCompDisplayed
为false
,因此隐藏了myCustomComp
。 myCustomComp
设置为undefined
。
点击后,执行displayAndUseMyCustomComp
功能,isMyCustomCompDisplayed
设置为true
。 Angular需要更改检测周期来更新myCustomComp
查询列表。但是,您尝试立即读取该值,因此它仍然是undefined
。您需要等待Angular的另一个更改检测周期来更新查询列表。
setTimeout
如果您尝试在超时内阅读myCustomComp
,Angular有机会在更新isMyCustomCompDisplayed
和您阅读myCustomComp
之间运行更改检测。
当Angular运行MainComponent
的更改检测时,它检测到isMyCustomCompDisplayed
已更新。所以它会更新ngIf
的绑定。它反过来对此更改做出反应,并使用myCustomComp
创建并嵌入视图,并将其附加到MainComponent
组件:
@Input()
set ngIf(condition: any) {
if (condidition) {
this._viewContainer.createEmbeddedView(this._thenTemplateRef, this._context);
如果您正在寻找同步解决方案,它将在所有生命周期挂钩中可用,这些挂钩在执行后执行的下一个更改检测周期中更新视图子查询列表之后执行displayAndUseMyCustomComp
。目前这些是ngAfterViewInit
和ngAfterViewChecked
。由于前者只被调用一次,我们需要使用ngAfterViewChecked
:
ngAfterViewChecked() {
if(this.isMyCustomCompDisplayed) {
console.log(this.myCustomComp) // prints {object}
}
}
displayAndUseMyCustomComp() {
this.isMyCustomCompDisplayed = true
}
@ThinkingMedia
建议的另一个同步解决方案也很好。您可以使用ViewChildren
代替ViewChild
订阅changes
(顺便提一下您的模板参考):
@ViewChildren(myCustomComp) as: QueryList<myCustomComp>;
ngAfterViewInit() {
this.myCustomComp.changes.subscribe(() => {
console.log(this.myCustomComp.first); // prints {object}
});
}
当Angular将更新查询列表(稍早于ngAfterViewChecked
)时,将在下一个摘要期间触发回调。
如果您正在寻找异步解决方案,请在执行此操作时使用setTimeout
。 Promise.resolve(null).then(()=>{ console.log(this.myCustomComp) })
(微任务)解决方案无法正常工作,因为它将在当前堆栈之后但在更改检测之前执行。
有关变更检测的更多信息,请阅读 Everything you need to know about change detection in Angular