尽管我们在父组件中更改了两次属性的值,但 ngOnChanges 没有被调用两次

Posted

技术标签:

【中文标题】尽管我们在父组件中更改了两次属性的值,但 ngOnChanges 没有被调用两次【英文标题】:ngOnChanges not getting called twice although we are changing the value of property twice in parent component 【发布时间】:2020-01-31 13:19:38 【问题描述】:

有两个组件,即 ParentComponent 和 ChildComponent。我们将一个变量从父组件绑定到子组件。根据角度文档,只要父组件中的属性值发生更改,就会调用子组件的“ngOnChanges”。现在在 ParentComponent 中,我们两次更改该变量的值,但在 ChildComponent 中,“ngOnChanges”只被调用一次。

父组件如下:

父组件.html

<p>parentcomponent works!</p>
<button (click)="onClicking()">Click here</button>
<app-childcomponent [temp]="inputfromparent"></app-childcomponent>

父组件.ts

import  Component, OnInit  from '@angular/core';

@Component(
  selector: 'app-parentcomponent',
  templateUrl: './parentcomponent.component.html',
  styleUrls: ['./parentcomponent.component.css']
)
export class ParentcomponentComponent implements OnInit 

  private inputfromparent = "B"
  constructor()  

  ngOnInit() 
  

  onClicking()
    this.inputfromparent = "C"; //line1
    console.log("Hello");
    this.inputfromparent= "D"; //line2
  

子组件如下:

子组件.ts

import  Component, OnInit, OnChanges, Input  from '@angular/core';

@Component(
  selector: 'app-childcomponent',
  templateUrl: './childcomponent.component.html',
  styleUrls: ['./childcomponent.component.css']
)
export class ChildcomponentComponent implements OnInit, OnChanges

  @Input() private temp = "A";
  constructor()  

  ngOnInit() 
  

  ngOnChanges(change)
    var test = change;
    console.log(test);
    console.log(this.temp);
  

在 ParentComponent.ts 文件中,每当在 ParentComponent.html 文件中定义的单击按钮上调用“onClicking”方法时,我们都会更改“inputfromparent”的值两次(参见第 1 行和第 2 行)。由于我们将此变量与 ChildComponent.ts 文件的变量“temp”绑定,因此 ChildComponent.ts 文件的“ngOnChanges”应该按照角度文档调用两次,如下所示:

A lifecycle hook that is called when any data-bound property of a directive changes. Define an ngOnChanges() method to handle the changes.

但 ChildComponent.ts 文件的“ngOnChanges”仅在 ParentComponent.html 文件中定义的单击按钮上调用“onClicking”时仅被调用一次。

我的疑问是,由于我们在 ParentComponent.ts 文件的“onClicking”方法中更改了“inputfromparent”的值两次,所以 ChildComponent.ts 文件的“ngOnChanges”应该被调用两次。但它只被调用一次。

【问题讨论】:

【参考方案1】:
  onClicking() 
    this.inputfromparent = "C"; //line1
    console.log("Hello");
    this.inputfromparent= "D"; //line2
  

这是同步运行的。也就是说,在onClicking 结束运行之前不会执行其他代码:对于整个外部世界(模板引擎、更改检测等),onClicking() 被调用,在它之前,inputfromparent 的值是“B ”,然后是“D”。两者之间几乎是一个黑匣子。

【讨论】:

但是在第 1 行,我们将“inputfromparent”的值从“B”更改为“C”。因此,此时更改检测周期也应该运行 ChildComponent.ts 文件的“ngOnChanges”。那么为什么此时没有调用它(当“inputfromparent”的值更新为“C”时)? 更改检测不在第 1 行和第 2 行之间运行,因为 nothing 在第 1 行和第 2 行之间运行,除了 console.log 感谢您的解释。但是您能否解释一下为什么更改检测不能在第 1 行和第 2 行之间运行,尽管我们正在更改父组件中的属性值,因此根据角度文档,更改检测器应该运行? 尝试阅读此blog.thoughtram.io/angular/2016/02/22/… 供初学者使用。【参考方案2】:

你应该调用 changedetector 否则你会从 angular 中得到异常。请检查控制台。

export class ParentcomponentComponent implements OnInit 

  private inputfromparent = "B";
  constructor(private cd: ChangeDetectorRef)  

  ngOnInit() 
  

  onClicking()
    this.inputfromparent = "C"; //line1
    this.cd.detectChanges();
    console.log("Hello");
    this.inputfromparent= "D"; //line2
    this.cd.detectChanges();
  

一些想法: inputfromparent 应该是公开的,以避免使用 AOT 进行生产构建 ChildComponent 应使用 OnPush 更改检测。

【讨论】:

为什么当 Angular 检测到父组件中的属性发生任何变化时,我们需要显式调用“detectChanges()”?请再次阅读我的疑问并尝试清除它。 如果你改变了它的值,changedetector 将完成它的工作,然后再次检查它的值以确定它是否已经改变,如果它已经改变抛出一个类似这样的异常: ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed检查后。以前的值:'ngIf: C'。当前值:'ngIf: D​​'。我假设您可以在控制台上看到它。

以上是关于尽管我们在父组件中更改了两次属性的值,但 ngOnChanges 没有被调用两次的主要内容,如果未能解决你的问题,请参考以下文章

在父组件的值更改时更新子组件中的值

当我通过鼠标单击更改 SwiftUI 列表的选择时,@Published 属性的 didSet 被调用了两次

如何存储子组件发出的值?

为 kendo ui Combobox 触发了两次更改事件

如何在父级下访问v-slot的值——vuejs

Lambda 函数在 DynamoDB 事件上触发了两次