【问题标题】:Angular 9 - refresh view ngOnChanges. Why do i need to use ChangeDetectorRef?Angular 9 - 刷新视图 ngOnChanges。为什么我需要使用 ChangeDetectorRef?
【发布时间】:2021-09-15 16:41:13
【问题描述】:

在我的场景中,我有一个父组件在按钮单击时接收新数据。子组件通过@Input 字段接收这些数据。在子组件事件ngOnChanges 被触发。 isLoaded 字段的值按预期更新。问题是:为什么我的 HTML 条件 <div *ngIf="isLoaded"> 需要 this.cd.detectChanges() 才能注意到 isLoaded 标志已更改?

父组件:

@Component()
export class ParentComponent implements OnInit {
  data: MyData;

  ngOnInit(): void {
  }

  updateWhenClickButton() {
    this.someService.getData(this.a, this.b, this.c).then(dataabc =>{
      this.data = new PurchasePriceBoxPlotData(this.a, this.b, dataabc);
    });
  }
}

父 HTML 内容:

<child-component [data]="data"></child-component>

子组件:

@Component()
export class ChildComponent implements OnInit, OnChanges {

  @Input()
  data: MyData;
  
  isLoaded: boolean = false;

  constructor(private cd: ChangeDetectorRef) { }

  ngOnInit(): void {
     this.doSth();
  }

  ngOnChanges(changes: SimpleChanges): void {
    this.isLoaded = false;
    this.cd.detectChanges();
    this.doSth();
  }
  
  private doSth() {
    ....
    this.isLoaded = true;
  }
}

子 HTML:

<div *ngIf="isLoaded">
    <custom-component [data]="data"></custom-component>
</div>

编辑

  public isLoaded$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);

  ngOnChanges(changes: SimpleChanges): void {
      this.isLoaded$.next(false);
      this.dosth();
      this.isLoaded$.next(true);
  }

<div *ngIf="isLoaded$ | async">

【问题讨论】:

  • 能给个stackblitz示例代码吗?
  • 你真的不应该正常使用 - 你在链上的某个地方使用 onPush 吗?另外 - 你在这里做了一些奇怪的事情。为什么从 onChanges 调用 ngOninit 以及为什么假设 onChanges 触发加载时为假?
  • @MikeOne 1. 我没有使用 onPush。 2. ngOninit 只是因为我想在每次出现新数据时重新创建子组件。这可能不是最好的主意,因为在创建组件时应该调用一次 oninit。也许我应该只更新在 onInit 期间创建的设置,而不是每次都从头开始创建它们。 3. 就像我在#2 中所说的那样。这个标志让我用更新的数据/设置重新创建子组件。是的,我知道这可能不是最佳方式,但我只是在学习 Angular 并寻求改进。
  • 默认情况下更改检测发生在ngOnChanges 的末尾,不是吗?

标签: angular


【解决方案1】:

Angular 的变更检测比最初看起来要复杂一些。这是一篇可能会有所帮助的好文章,

https://blog.thoughtram.io/angular/2016/02/01/zones-in-angular-2.html

即使使用默认的 ChangeDetectionStrategy,您也可以做一些事情,但 Angular 的变更检测不会发现这些事情,我认为您遇到了其中之一。

我建议您使用 BehaviorSubject 而不是简单的布尔值,

public BehaviorSubject<boolean> isLoaded$ = new BehaviorSubject<boolean>(true);

然后在你的函数中,

ngOnChanges(): void {
  this.isLoaded$.next(false);
  ...
  this.isLoaded$.next(true);
}

最后在您的模板中,

<div *ngIf="isLoaded$ | async">
  <custom-component [data]="data"></custom-component>
</div>

您无需以这种方式运行cd.detectChanges(),因为BehaviorSubject 将值直接流式传输到模板。

另外,作为旁注,我永远不会调用this.ngOnInit(),该函数是一个钩子,旨在在组件生命周期的特定点运行,手动调用它可能会导致各种混乱.

【讨论】:

  • 谢谢,我去看看。
  • 我检查了,似乎它仍然无法正常工作。在我的评论中添加了您的更改。它在第一次正确加载页面,但在值更改时不再刷新视图。
  • 唯一有效的是 this.cd.detectChanges();在 this.isLoaded = false 之后;我不知道为什么 this.isLoaded = true;不需要单独检测更改
猜你喜欢
  • 2017-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-18
  • 1970-01-01
  • 2019-08-24
  • 1970-01-01
相关资源
最近更新 更多