繁体   English   中英

Angular 4 ExpressionChangedAfterItHasBeenCheckedError

[英]Angular 4 ExpressionChangedAfterItHasBeenCheckedError

在父组件中=>

ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: ''. Current value: '[object Object]'.
            at viewDebugError (vendor.bundle.js:8962)
            at expressionChangedAfterItHasBeenCheckedError (vendor.bundle.js:8940)

父组件 Html

<div>
  <app-child-widget [allItems]="allItems" (notify)="eventCalled($event)"></app-child-widget>
<div>

父组件

export class ParentComponent implements OnInit {

  returnedItems: Array<any> = [];
  allItems: Array<any> = [];

  constructor(
  ) { }

  ngOnInit() {
     this.allItems = // load from server...
  }

  eventCalled(items: Array<any>): void {
    this.returnedItems = items;
  }
}

子组件

@Component({
  selector: 'app-child-widget',
  templateUrl: 'child.component.html',
  styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit {
  @Output() notify: EventEmitter<any> = new EventEmitter();
  @Input() private allItems: Array<any>;

  constructor() { }

  ngOnInit() {
    doSomething();
  }

  doSomething() {
    this.notify.emit(allItems);
  }
}

文章关于ExpressionChangedAfterItHasBeenCheckedError错误你需要知道的一切都非常详细地解释了这种行为

原因

您的问题与问题非常相似但不是通过服务更新父属性,而是通过同步事件广播更新它。 这是链接答案中的引用:

在摘要循环期间,Angular 会对子指令执行某些操作。 其中一项操作是更新输入并调用子指令/组件上的 ngOnInit 生命周期钩子。 重要的是这些操作是按照严格的顺序执行的:

  • 更新输入
  • 调用 ngOnInit

因此,在您的情况下,Angular 更新了子组件上的输入绑定allItems ,然后在子组件上调用了onInit ,这导致了对父组件的allItems的更新。 现在你有数据不一致。 父组件有一个值,而子组件有另一个值。 如果 Angular 继续同步更改,您将获得无限循环。 这就是为什么在下一个更改检测周期 Angular 检测到allItems已更改并引发错误的原因。

解决方案

这似乎是一个应用程序设计缺陷,因为您正在更新父组件和子组件的details 如果不是,那么您可以通过像这样异步发出事件来解决问题:

export class ChildComponent implements OnInit {
  @Output() notify: EventEmitter<any> = new EventEmitter(true);
                                                        ^^^^^^-------------

但是你必须非常小心。 如果您使用任何其他钩子,如在每个摘要循环中调用的ngAfterViewChecked您最终将陷入循环依赖!

就我而言,我正在更改数据的状态——这个答案要求你阅读 AngularInDepth.com 对摘要循环的解释——在 html 级别内,我所要做的就是像这样改变我处理数据的方式:

<div>{{event.subjects.pop()}}</div>

进入

<div>{{event.subjects[0]}}</div>

总结:不是弹出——它返回并删除数组的最后一个元素从而改变我的数据的状态——我使用了正常的方式来获取我的数据而不改变它的状态从而防止异常。

当出现此错误时,您可以使用rxjs timer将调用延迟一小段时间。

此示例使用@angular/material stepper从 url 中的查询参数加载默认步骤。

import { timer } from 'rxjs'

@Component({
  template: `
    <mat-horizontal-stepper #stepper>
      <mat-step></mat-step>
      <mat-step></mat-step>
      <mat-step></mat-step>
    </mat-horizontal-stepper>
  `
})
export class MyComponent {
  @ViewChild('stepper', { static: true })
  private stepper: MatHorizontalStepper

  public ngOnInit() {
    timer(10).subscribe(() => {
      this.activatedRoute.queryParams.subscribe(params => {
        this.stepper.selectedIndex = parseInt(params.step || 0)
      })
    })
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM