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

时间:2020-08-04 22:00:38

标签: angular angular-ng-if viewchild

我遇到了以下问题,该问题已由{static: false}中的@ViewChild属性修复。 这个How should I use the new static option for @ViewChild in Angular 8?有助于解决堆栈溢出问题。

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

我想出了一个堆炸弹,它说明了我不理解的东西。 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的第二行值显示为undefined。设置this.display = false之后是否应该不进行更改检测,因此不应将其不确定?
  2. 为什么this.trueViewChild保持未定义状态。我希望它在*ngIf变为true之后找到元素?

1 个答案:

答案 0 :(得分:1)

角度变化检测借助zone.js库工作。 更新ViewChild / Content查询发生在更改检测周期内

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

例如,它可以监听单击事件并在此任务完成(没有待处理的任务,这意味着区域变得稳定)后发出通知。

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的第二行值显示为未定义。设置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中有此规则:

对于静态查询(static:true),查询会在视图一次解析 已创建,但在运行更改检测之前。结果, 但是,将永远不会更新以反映对视图的更改,例如 更改为ngIf和ngFor块。

这意味着,如果最初是false(例如,它位于* ngIf或ng-template内),则它将始终为false