Angular 6 - Expression has changed after it was ch

2020-06-16 09:22发布

问题:

I am new to angular and trying to combine with easyui angular.

I found this error when trying to open/close dialog:

ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'klass: l-btn f-inline-row f-content-center l-btn-small l-btn-focus'. Current value: 'klass: l-btn f-inline-row f-content-center l-btn-small'.

And here is my code.

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);
      }
    }
  }
}

I have search many tutorial to fix this problem but it seems like the tutorial condition doesn't match with mine.

Do i misunderstand something or missing something practical about angular? what is the proper way to update attribute?

回答1:

Use ChangeDetectorRef Service to detect new changes

When a view uses the OnPush (checkOnce) change detection strategy, explicitly marks the view as changed so that it can be checked again.

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

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

})


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

     this.cdRef.detectChanges();
      }


回答2:

Can't able to see the open/close dialog code. Here is explanation for the issue. Usually Angular Updates the DOM based on Model object with help of Change Detection. Angular updates the DOM and it got reflected in UI, After that Model also got updated.

Angular don't know what needs to do, so it's reporting an error. We need to manually trigger the change detection in order to reflect the DOM. Try to wrap the section of code inside setTimeout Block

setTimeout(()=>{

});


回答3:

I was getting this error after modifying a Map object that was being iterated over using

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

The fix that worked for me was just to add ChangeDetectionStrategy.OnPush to my component declaration:

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


回答4:

I believe the issue is you are initializing form for users. So the issue is that the form is building first and then users are getting loaded so angular is detecting changes so it's throwing an error. Ideally this.cdRef.detectChanges(); should work. Also I have added a check in HTML that component will load only after users list are fetched from service.

This might help you :

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" >