【问题标题】:Pass a variable between 2 children components produces ExpressionChangedAfterItHasBeenCheckedError error在 2 个子组件之间传递变量会产生 ExpressionChangedAfterItHasBeenCheckedError 错误
【发布时间】:2019-07-18 19:14:24
【问题描述】:

我有 2 个必须传递信息的子组件。

parent.component.html

<childA [loading]="loading">
    <childB (loadingChanged)="loadingChangedHandler($event)"></childB>
</childA>

parent.component.ts

loading = false;
constructor(){
   ......
}
loadingChangedHandler(loadingChild: boolean){
    this.loading = loadingChild;
  }

childB.component.ts(传递一个变量给父级,这个值传给子级A)

....
@Output() loadingChanged: EventEmitter<boolean> = new EventEmitter();
....
loadData() {
    this.loadingChanged.emit(true);
      this.service.readDate()
        .subscribe((data: any) => {
            ........
            this.loadingChanged.emit(false);
        })

  }

最后在 childA 组件中,我必须接收变量并执行操作

childA.component.ts

@Input() loading: boolean = false
constructor() {
....}
....

在子 A 中,我有一个可以工作的加载微调器,但我在控制台中收到一条错误消息:

错误错误:ExpressionChangedAfterItHasBeenCheckedError:表达式 检查后有变化。以前的值:“加载:假”。 当前值:'正在加载:true'。

我该如何解决这个问题? 有关于它的文档(ngAfterViewInit),但我不明白为什么它在我的情况下不起作用。

【问题讨论】:

标签: angular typescript


【解决方案1】:

Angular 运行更改检测,当它发现某些已传递给子组件的值已更改时,Angular 会抛出错误 ExpressionChangedAfterItHasBeenCheckedError Read here in detail

为了解决您的问题,您可以使用 childA.component.ts

中的 AfterContentChecked 生命周期挂钩
import { ChangeDetectorRef, AfterContentChecked} from '@angular/core';

  constructor(
  private cdref: ChangeDetectorRef) { }

  ngAfterContentChecked() {

    this.cdref.detectChanges();

  }

【讨论】:

  • 我另外添加了导出类 ChildA 实现 OnInit、AfterContentChecked ...,但仍然不起作用。此时,我只接收到 ChildA 中加载的 @Input() 的值,但我没有对它做任何事情并且错误仍然存​​在
  • 如果有效,则在控制台中将错误标记在 行中
【解决方案2】:

Imp:Angular,在开发模式下运行两次更改检测。

由于在第一个更改检测阶段,它识别出父组件的加载属性已更改,并且在下一个更改检测周期中,它观察到子组件再次更改了加载属性,因此它抛出异常,因为加载在第一个周期检查完所有属性后更改了属性。

在生产模式下,您不会收到此错误,因为在 prod 模式下更改检测只运行一次。这就是为什么打开开发模式很重要,以便我们提前知道这些类型的错误,这些错误会导致数据和视图不一致。

将修改加载属性的代码放入 setTimeout 可确保代码在上一个更改检测完成时的下一个滴答中运行。

试着把 this.loadingChanged.emit(false);在 setTimeout 中为 0。

setTimeout(() => {
 this.loadingChanged.emit(false);
}, 0);

【讨论】:

  • 您的解决方案有效,但将 setTimeout(() => { this.loadingSubfamiliesChanged.emit(true); }, 0); this.service.rea... 在不更改 ChildA 组件中的代码的情况下,您能解释一下这是什么原因吗?
  • 我正在更新你为什么需要 setTimeout 的答案。
  • 您能解释一下 ChildA 组件中使用 ngAfterContentChecked() 或 ngAfterViewInit() 的解决方案不起作用的原因吗?我找到了这个link,但我不明白为什么 setTimeout 的工作原理类似于 ngAfterViewInit() @Ritesh
猜你喜欢
  • 2017-07-06
  • 2016-06-23
  • 2023-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-21
  • 1970-01-01
  • 2021-10-15
相关资源
最近更新 更多