简体   繁体   English

Angular4如何知道ViewChild何时被重置

[英]Angular4 How to know when a ViewChild has been reset

Here is the template of main.html : 这是main.html的模板:

<button (click)="displayAndUseMyCustomComp()"></button>
<my-custom-comp *ngIf="isMyCustomCompDisplayed" #myCustomComp="myCustomComp"></my-custom-comp>

and main.component.ts : 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}
     })
  }

}

What's happening is that my template isn't yet refreshed after I set isMyCustomCompDisplayed to true . 发生了什么事情,我将isMyCustomCompDisplayed设置为true后,我的模板尚未刷新。 However, if I use a setTimeout , myCustomComp gets updated and my issue goes away. 但是,如果我使用setTimeoutmyCustomComp会更新,我的问题就会消失。 It is midly hacky, and I was wondering what was the correct way of doing what I am trying to. 这是中等的hacky,我想知道我正在尝试做什么的正确方法。

Here is why it's undefined after you call displayAndUseMyCustomComp 这就是为什么在调用displayAndUseMyCustomComp后它未定义的原因

Angular updates ViewChild query list as part of change detection. Angular将ViewChild查询列表更新为更改检测的一部分。 When Angular was running initial change detection the isMyCustomCompDisplayed was false and so myCustomComp was hidden. 当Angular运行初始更改检测时, isMyCustomCompDisplayedfalse ,因此隐藏了myCustomComp The myCustomComp was set to undefined . myCustomComp设置为undefined

After you make a click the function displayAndUseMyCustomComp is executed and isMyCustomCompDisplayed is set to true . 单击后,执行displayAndUseMyCustomComp函数,并将isMyCustomCompDisplayed设置为true Angular requires a change detection cycle to update the myCustomComp query list. Angular需要更改检测周期来更新myCustomComp查询列表。 However, you try to read the value immediately and so it's still undefined . 但是,您尝试立即读取该值,因此仍未undefined You need to wait for another change detection cycle for Angular to update the query list. 您需要等待Angular的另一个更改检测周期来更新查询列表。

Here is why it's working if you wrap the call into setTimeout 如果将调用包装到setTimeout这就是它的工作setTimeout

If you try to read the myCustomComp inside the timeout, Angular has a chance to run change detection between the update to isMyCustomCompDisplayed and the time you read myCustomComp . 如果您尝试在超时内读取myCustomComp ,Angular有机会在更新isMyCustomCompDisplayed和读取myCustomComp之间运行更改检测。

Here is what happens during that change detection cycle 以下是该变化检测周期中发生的情况

When Angular runs change detection for the MainComponent it detects that isMyCustomCompDisplayed is updated. 当Angular运行MainComponent更改检测时,它会检测到isMyCustomCompDisplayed已更新。 So it goes and updates bindings for ngIf . 所以它会更新ngIf绑定。 It in turn reacts to this change and creates and embedded view with the myCustomComp and attaches it to the MainComponent component: 它反过来对此更改做出反应,并使用myCustomComp创建和嵌入视图,并将其附加到MainComponent组件:

  @Input()
  set ngIf(condition: any) {
      if (condidition) {
          this._viewContainer.createEmbeddedView(this._thenTemplateRef, this._context);

When is the updated query list available 何时更新的查询列表可用

If you're looking for synchronous solution, it will be available inside all lifecycle hooks that are executed after the view children query list is updated during the next change detection cycle that follows execution of displayAndUseMyCustomComp . 如果您正在寻找同步解决方案,它将在执行displayAndUseMyCustomComp的下一个更改检测周期中更新视图子查询列表后执行的所有生命周期钩子可用。 At the moment these are ngAfterViewInit and ngAfterViewChecked . 目前这些是ngAfterViewInitngAfterViewChecked Since the former is called only once, we need to use ngAfterViewChecked : 由于前者只被调用一次,我们需要使用ngAfterViewChecked

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

  displayAndUseMyCustomComp() {
     this.isMyCustomCompDisplayed = true
  }

Another synchronous solution suggested by @ThinkingMedia is also good. @ThinkingMedia建议的另一个同步解决方案也很好。 You can use ViewChildren instead of ViewChild and subscribe to changes (btw you don't template reference): 您可以使用ViewChildren而不是ViewChild并订阅changes (顺便ViewChild您没有模板引用):

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

  ngAfterViewInit() {
    this.myCustomComp.changes.subscribe(() => {
      console.log(this.myCustomComp.first); // prints {object}
    });
  }

The callback will be triggered during next digest when Angular will be updating query list (slightly earlier than ngAfterViewChecked ). 当Angular将更新查询列表(稍早于ngAfterViewChecked )时,将在下一个摘要期间触发回调。

If you're looking for asynchronous solution, use setTimeout as you do it. 如果您正在寻找异步解决方案,请在执行此操作时使用setTimeout The Promise.resolve(null).then(()=>{ console.log(this.myCustomComp) }) (microtask) solution won't work because it will be executed after the current stack but before the change detection. Promise.resolve(null).then(()=>{ console.log(this.myCustomComp) }) (microtask)解决方案将无法工作,因为它将在当前堆栈之后但在更改检测之前执行。

For more information on change detection read 有关更改检测读取的更多信息
Everything you need to know about change detection in Angular . 您需要了解的有关Angular中的更改检测的所有信息

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM