Angular 6 - 检查后表达式已更改

17

我是一名新手,正在尝试将easyui angular与angular结合使用。

当我尝试打开/关闭对话框时,出现了以下错误:

ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: 表达式在检查后已更改。之前的值为:'klass: l-btn f-inline-row f-content-center l-btn-small l-btn-focus'。当前值为: 'klass: l-btn f-inline-row f-content-center l-btn-small'。

以下是我的代码:

user.component.html

<eui-linkbutton (click)="addAction()" iconCls="icon-add" style="margin-bottom:10px">Add New</eui-linkbutton>
<eui-datagrid 
  [data]="users" style="height1:250px" [filterable]="true" 
  [selectionMode]="'single'"
  [(selection)]="rowSelected"
  [euiContextMenu]="contextMenu"
  >

</eui-datagrid>


<!-- contextmenu -->
<eui-menu #contextMenu (itemClick)="contextClick($event)">
    <eui-menu-item value="edit" text="Edit"></eui-menu-item>
    <eui-menu-item text="Delete"></eui-menu-item>
    <eui-menu-item text="View"></eui-menu-item>
</eui-menu>


<eui-dialog #formDialog [closed]="closed" [draggable]="true" [modal]="true" [title]="isNewRow ? 'Add' : 'Edit'" (close)="closed=true">

  <div class="dialog-button">
    <eui-linkbutton [disabled]="!formObj.valid" (click)="saveAction()" style="width:80px">Save</eui-linkbutton>
    <eui-linkbutton (click)="formDialog.close()" style="width:80px">Cancel</eui-linkbutton>
  </div>
</eui-dialog>

user.component.ts

export class UserComponent implements OnInit {
  rowSelected = null;
  formObj: FormGroup;

  constructor(public userService: UserService, fb: FormBuilder){

    }

  get row(){
      return this.rowSelected;
  }
  set row(value: any){
      this.rowSelected = value;
      this.formObj.reset(this.rowSelected);
  }

  editAction(row){
    this.row = this.rowSelected;
    this.closed = false;
  }

  addAction(){
    console.log('open babe');
    this.row = {
      'id' : null,
      'username' : null,
      'password' : null,
      'email' : null,
      'first_name' : null,
      'last_name' : null
    };

    this.closed = false;
  }


  isNewRow = false;
  editingRow = null;
  closed = true;
  users: Object;

  currentSelection = null;
  ngOnInit() {
    this.userService.getData().subscribe(
      data => this.users = data
    ) 
    console.log('ngoninit');

    this.initRow();
    this.closed = true;
  }

  initRow() {
    this.editingRow = {
      username: null,
      password: null,
      email: null,
      first_name: null,
      last_name: null,
    };
  }

  onAddRow() {
    console.log('open babe');
    this.initRow();
    this.isNewRow = true;
    this.closed = false;
  }

  onEditRow(row) {
    this.isNewRow = false;
    this.editingRow = row;
    this.closed = false;
  }
   contextClick(value){
    if(!this.rowSelected){
      alert('Please select a row first!');
    }
    else{
      if(value == 'edit'){
        this.editAction(this.rowSelected);
      }
    }
  }
}

我搜索了很多教程来解决这个问题,但似乎教程的条件与我的不匹配。

我是否理解错了一些关于angular的实用知识?更新属性的正确方法是什么?


https://dev59.com/pq3la4cB1Zd3GeqPMm_3#51712109 - Chellappan வ
1
this.cdRef.detectChanges(); 放在 afterContentInitngDocheck 中。除此之外,在开发阶段而不是生产阶段,你会遇到这个错误,因为 Angular 在开发模式下运行了两次变更检测循环。 - Vikas
@Vikas同意这只会在开发模式下发生的观点,但是必须要注意这个警告或错误,否则会导致生产环境中组件之间的数据绑定不一致。 - Amit Chigadani
@AmitChigadani 是的,我完全同意你的看法:) - Vikas
5个回答

22

使用ChangeDetectorRef服务来检测新的更改。

当一个视图采用了OnPush(checkOnce)变更检测策略时,明确标记该视图已更改,以便可以再次进行检查。

import { Component, Input, ChangeDetectionStrategy,ChangeDetectorRef } from '@angular/core';

@Component({
  selector: 'component',
  templateUrl: 'component.html',
  changeDetection: ChangeDetectionStrategy.OnPush

})


   constructor(cdRef:ChangeDetectorRef){} 
   ngAfterViewInit() {

     this.cdRef.detectChanges();
      }

我已经尝试过了,但是仍然出现相同的错误,另外我的应用程序在执行操作时似乎卡顿且无响应。 - Ryan Arief
对不起先生,我再试了一次但结果相同。这里有一个要点,我的应用程序运行良好,但开发工具中的错误日志却让我很困扰。 - Ryan Arief
错误 Error: ExpressionChangedAfterItHasBeenCheckedError: 检查后表达式已更改。之前的值为:'klass: l-btn f-inline-row f-content-center l-btn-small l-btn-focus'。当前值为:'klass: l-btn f-inline-row f-content-center l-btn-small'。 - Ryan Arief

12

在使用迭代器遍历一个Map对象时,修改了该对象而导致出现了该错误。

*ngFor="let entry of myMap.entries()"

对我有用的解决方法就是在我的组件声明中添加ChangeDetectionStrategy.OnPush:

@Component({
  selector: 'component',
  templateUrl: 'component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})

7

无法看到打开/关闭对话框代码。以下是问题的说明。通常,Angular根据模型对象通过变更检测来更新DOM。Angular会更新DOM并在UI中反映出来,之后模型也会得到更新。

Angular不知道需要做什么,因此报告了错误。我们需要手动触发变更检测以反映DOM。尝试将代码部分包装在setTimeout块中。

setTimeout(()=>{

});

我试图包含所有的代码,但是SO系统拒绝了我的问题,并说我的代码太多了(类似这样的话)。所以我删除了一些代码,似乎过度了。 - Ryan Arief
我需要包装哪个部分? - Ryan Arief
你正在调用打开对话框的函数。 - Suresh Kumar Ariya

3
我相信问题出在您正在为用户初始化表单。所以问题是表单会首先构建,然后才会加载用户,因此Angular检测到变化并抛出错误。理想情况下this.cdRef.detectChanges();应该起作用。此外,我已在HTML中添加了一个检查,只有在从服务获取用户列表后,组件才会加载。
以下内容可能对您有所帮助: TS:
     constructor(cdRef:ChangeDetectorRef,public userService: UserService, fb: FormBuilder){
     }

     ngOnInit() {
        this.userService.getData().subscribe(
          data => {
             this.users = data;
             this.initRow();
             this.cdRef.detectChanges();
          }
        ); 
        console.log('ngoninit');
        this.closed = true;
      }

HTML :

 <eui-datagrid *ngIf="users && users.length > 0"
      [data]="users" style="height1:250px" [filterable]="true" 
      [selectionMode]="'single'"
      [(selection)]="rowSelected"
      [euiContextMenu]="contextMenu" >

1
你可以添加一些描述来解释这段代码将如何解决问题。 - Amit Chigadani

1

对于仍在寻找答案的人,我终于找到了解决方案!

我遇到了这个错误,并通过在 ngAfterViewInit() 中添加 this.changeDetector.detectChanges() 来解决它。

import { AfterViewInit, ChangeDetectorRef, OnDestroy, OnInit } from '@angular/core';

...

export class ClassName implements AfterViewInit, OnDestroy, OnInit {

  ...

  constructor(
    private changeDetector: ChangeDetectorRef
  ) {
    // Code here to populate fields, which are bound
    // in the html, from within a Subscription.
    // This was the source of my error.
  }

  ...

  ngAfterViewInit() {
    this.changeDetector.detectChanges();
  }
}

在实现了上述代码之后,错误消失了!请参阅以下链接以获取更多详细信息:

https://gist.github.com/vades/a225170304f34f88a7a5d48bf4b1f58c


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接