Angular4如何知道何时重置ViewChild

Sci*_*ion 2 typescript angular

这是模板main.html:

<button (click)="displayAndUseMyCustomComp()"></button>
<my-custom-comp *ngIf="isMyCustomCompDisplayed" #myCustomComp="myCustomComp"></my-custom-comp>
Run Code Online (Sandbox Code Playgroud)

和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}
     })
  }

}
Run Code Online (Sandbox Code Playgroud)

设置isMyCustomCompDisplayed 为之后,我的模板尚未刷新true。不过,如果我使用setTimeout,myCustomComp更新,并且我的问题就会消失。这是中庸之举,我想知道做我想做的正确方法是什么。

Max*_*kyi 8

这就是为什么您致电后未定义的原因 displayAndUseMyCustomComp

角度更新ViewChild查询列表作为更改检测的一部分。当Angular运行初始更改检测时,isMyCustomCompDisplayedwas 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);
Run Code Online (Sandbox Code Playgroud)

更新的查询列表何时可用

如果您正在寻找同步解决方案,那么它将在所有生命周期挂钩中可用,该挂钩在执行的下一个更改检测周期内更新视图子查询列表后执行的所有生命周期挂钩中都可用displayAndUseMyCustomComp。目前,这些是ngAfterViewInit和ngAfterViewChecked。由于前者仅被调用一次,因此我们需要使用ngAfterViewChecked:

  ngAfterViewChecked() {
    if(this.isMyCustomCompDisplayed) {
        console.log(this.myCustomComp) // prints {object}
    }
  }

  displayAndUseMyCustomComp() {
     this.isMyCustomCompDisplayed = true
  }
Run Code Online (Sandbox Code Playgroud)

提出的另一种同步解决方案@ThinkingMedia也不错。您可以使用ViewChildren而不是进行ViewChild订阅changes(顺便说一下,您没有模板引用):

  @ViewChildren(myCustomComp) as: QueryList<myCustomComp>;

  ngAfterViewInit() {
    this.myCustomComp.changes.subscribe(() => {
      console.log(this.myCustomComp.first); // prints {object}
    });
  }
Run Code Online (Sandbox Code Playgroud)

当Angular更新查询列表时(稍早于ngAfterViewChecked),回调将在下一个摘要期间触发。

如果您正在寻找异步解决方案,请照常使用setTimeout。该Promise.resolve(null).then(()=>{ console.log(this.myCustomComp) })(microtask)解决方案将无法工作,因为它会在当前堆栈之后,但在变化检测之前执行。

有关变更检测的更多信息,请阅读
Angular中有关变更检测所需的所有知识。

  • “ this.myCustomComp.changes.subscribe”这绝对救了我。非常感谢!!! (2认同)