为什么即使 *ngIf 设置为 true,@ViewChild 仍然未定义

Why is @ViewChild still undefined even though the *ngIf is set to true

我遇到了以下问题,已由 @ViewChild 中的 {static: false} 属性 修复。 这个 Whosebug Q/A 帮助

我想更好地了解这种情况,static 如何改变结果,以及变化检测如何影响这种情况。我在 angular 文档中阅读了一些关于变化检测的内容,发现它非常缺乏。

我想出了一个 stackblitz 来说明一些我不明白的事情。 Stackblitz angular example

单击 toggle 按钮两次时,我在命令行中得到以下信息:

> undefined undefined
> undefined undefined
> undefined ElementRef {nativeElement: div}
> undefined ElementRef {nativeElement: div}

然而我预计:

> undefined undefined
> undefined ElementRef {nativeElement: div}
> ElementRef {nativeElement: div} ElementRef {nativeElement: div}
> ElementRef {nativeElement: div} ElementRef {nativeElement: div}

这是代码的逻辑——(请参阅 stackblitz 中的完整代码)

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  @ViewChild("contentPlaceholder", { static: true })
  trueViewChild: ElementRef;

  @ViewChild("contentPlaceholder", { static: false })
  falseViewChild: ElementRef;

  display = false;

  constructor() {}

  show() {
    console.log(this.trueViewChild, this.falseViewChild);
    this.display = true;
    console.log(this.trueViewChild, this.falseViewChild);
  } 
}

我的问题是:

  1. 为什么 this.falseViewChild 的第二行值显示为未定义。在设置 this.display = false 之后更改检测不应该有 运行 并且因此它不应该是未定义的吗?
  2. 为什么 this.trueViewChild 保持未定义状态。我希望它在 *ngIf 变为 true?
  3. 后找到元素

Angular 变化检测在 zone.js 库的帮助下工作。 更新 ViewChild/Content 查询发生在更改检测周期

zone.js 库修补了异步 API(addEventListener、setTimeout()、Promises...)并确切知道执行了哪个任务以及何时完成。

比如可以在这个任务完成时监听点击事件并发出通知(没有pending任务,zone稳定)

Angular 订阅这些通知,以便对从根组件开始的三个所有组件执行更改检测

// your code 
(click)="someHandler()" 

someHandler() {              
 ....
}

// angular core
checkStable() {
  if (there is no any task being executed and there is no any async pending request) {
    PERFORM CHANGE DETECTION
  } 
}

代码中的顺序如下:

 click
  ||
  \/
someHandler()
  ||
  \/
checkStable()
  ||
  \/
PERFORM CHANGE DETECTION

那么,让我们来回答您的问题:

  1. 为什么this.falseViewChild的第二行值显示为undefined。在设置 this.display = false 之后更改检测不应该有 运行 因此它不应该是未定义的吗?

你改的时候没有反应display属性

show() {
 console.log(this.trueViewChild, this.falseViewChild);
 this.display = true;  <--- Angular doesn't do here anything, it only listens to zone state changes
 console.log(this.trueViewChild, this.falseViewChild); // nothing should be updated here 
                                                       // because there wasn't any cd cycle yet
} 

这就是您在第一次点击时得到以下输出的原因:

> undefined undefined
> undefined undefined   <---- nothing has updated

 ......
 update happens here

它稍后会更新,但除非您再次单击,否则您不会看到它,因为您稍后不会记录这些值。

  1. Why does this.trueViewChild stay undefined. I would expect it to find the element after the *ngIf becomes true?

因为Angular documentation中有这样的规则:

With static queries (static: true), the query resolves once the view has been created, but before change detection runs. The result, though, will never be updated to reflect changes to your view, such as changes to ngIf and ngFor blocks.

这意味着如果它最初是 false(例如,它在 *ngIf 或 ng-template 内)那么它将始终是 false