Angular4如何知道ViewChild何时被重置

时间:2017-07-06 11:49:32

标签: angular typescript

以下是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后,我的模板尚未刷新。但是,如果我使用setTimeoutmyCustomComp会更新,我的问题就会消失。这是中等的hacky,我想知道我正在做的事情的正确方法是什么。

1 个答案:

答案 0 :(得分:7)

以下是您致电displayAndUseMyCustomComp

后未定义的原因

角度更新ViewChild查询列表,作为更改检测的一部分。当Angular运行初始更改检测时,isMyCustomCompDisplayedfalse,因此隐藏了myCustomCompmyCustomComp设置为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。目前这些是ngAfterViewInitngAfterViewChecked。由于前者只被调用一次,我们需要使用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)时,将在下一个摘要期间触发回调。

如果您正在寻找异步解决方案,请在执行此操作时使用setTimeoutPromise.resolve(null).then(()=>{ console.log(this.myCustomComp) })(微任务)解决方案无法正常工作,因为它将在当前堆栈之后但在更改检测之前执行。

有关变更检测的更多信息,请阅读 Everything you need to know about change detection in Angular