ngx-datatable: ExpressionChangedAfterItHasBeenCheckedError

[x] bug report => search github for a similar issue or PR before submitting [ ] feature request [ ] support request => Please do not submit support request here, post on Stackoverflow or Gitter

Current behavior It’s looks like it relates to other library, but it’s interferes with ngx-datatable, at least I reproduced in that connection

When I show modal window from ng-bootstrap by handling (select) output I get ng:///NgxDatatableModule/DataTableBodyRowComponent.ngfactory.js:12 ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'datatable-body-cell sort-active active'. Current value: 'datatable-body-cell sort-active'.

it works fine when I show the same modal by (click) on some button on the page, but it raise each time I do that by (select), and when it happens, next modal activate break the page

Reproducing Fully reproduced with last angular and both packages here http://plnkr.co/edit/Ru8xoU3PNOqu4S1qUfbn?p=preview

About this issue

  • Original URL
  • State: open
  • Created 7 years ago
  • Reactions: 24
  • Comments: 42 (3 by maintainers)

Most upvoted comments

Same problem, resolved with blur…

event.target.closest('datatable-body-cell').blur();

I think this error is causing by the cell is deactive by popping up a modal, since it loses focus. The problem is the table component has been checked by angular and now it is changed by the modal. Therefore, it throws an error like ExpressionChangedAfterItHasBeenCheckedError.

To prevent this error, I think the best way is preventing the cell become active or as soon as it becomes active, deactivate it.

onActive(event) {
        (event.type === 'click') && event.cellElement.blur();
}

Getting the same error in a similar situation: instead of bringing up the dialog when the row is selected, I have a column that shows an icon (just a span) which has a click event handler to bring up the ng-bootstrap dialog. If I put the click handler on a button instead of the icon, it works.

Same issue I am facing that when i click on my icon in ngx-datatable column open modal it works fine but showing same error in my console.

ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'datatable-body-cell sort-active active'. Current value: 'datatable-body-cell sort-active'.

Do we have any updates regarding this issue?

As @maxisam noted, calling event.cellElement.blur() from (activate) works for me.

component.html

<ngx-datatable
    #datatable
    class="custom"
    [columns]="columns"
    [rows]="rows"
    [selected]="selected"
    [loadingIndicator]="loadingIndicator"
    [columnMode]="'force'"
    [headerHeight]="40"
    [footerHeight]="0"
    [rowHeight]="'auto'"
    [reorderable]="false"
    [sortType]="'single'"
    [selectionType]="'single'"
    [selectCheck]="singleSelectCheck"
    (activate)="onActivate($event)">
</ngx-datatable>

component.ts

onActivate(event: any) {
    if (event.type === 'dblclick') {
        event.cellElement.blur();
        this.edit(event); 
    }
}

I also encounter this problem today. I had to implement the workaround as others have mentioned above. My template:

                              prop="job_id"
                              [width]="80"
                              [sortable]="false">
            <ng-template let-row="row" ngx-datatable-cell-template>
                <span class="btn-link fake-link" (click)="getTranLog(row.job_id, $event)">{{row.job_id}}</span>
            </ng-template>
        </ngx-datatable-column>

And in my component:

		event.target.parentElement.parentElement.blur();
		this.modalRef                         = this._modalSvc.open(TranLogModalComponent);
		this.modalRef.componentInstance.jobID = jobID;
	}``` 

I’m not very proud of this solution, but the only way I got it to work is by blurring the datatable-body-cell right before emptyClickHandler main logic. We can target it by passing a reference to the template element and then looking up the parent of the parent (btnElement.parentElement.parentElement)

Something like this:

HTML:

<ngx-datatable-column name="Registration Date"
                         prop="registrationDate"
                         [resizeable]="false"
                         [flexGrow]="1">
       <ng-template let-row="row" let-value="value" ngx-datatable-cell-template>
       <button #btnElement
             class="btn-primary btn"
             (click)="emptyClickHandler(btnElement)">Edit</button>
       </ng-template>
</ngx-datatable-column>

JS:

emptyClickHandler(btnElement) {
     btnElement && btnElement.parentElement && btnElement.parentElement.parentElement &&
         btnElement.parentElement.parentElement.blur();

// ... Other logic here .. say to open a modal window

}

Same issue. I had to use both the onActivate from @maxtacco and @maxisam and the setTimeout from @ScottSpittle to get rid of the error. Neither worked for me by themselves.

Our case called for something a little different (because we needed to call event.stopPropagation() to prevent selection (onSelect) of the row (so @torontocode 's solution worked).

component.ts

...
public deleteField(event: any, row: any, firstChild: any): void {
    event.stopPropagation();
    firstChild && firstChild.parentElement && firstChild.parentElement.parentElement &&
      firstChild.parentElement.parentElement.blur();
    // open delete modal
    this.modalService.open(DeleteComponent);
    ...
}

public onSelect($event): void {
    // navigate to details page
}
...

component.html

<ngx-datatable-column>
  <ng-template ngx-datatable-cell-template let-row="row">
    <div #firstChild>
      <div (click)="deleteField($event, row, firstChild)" class="field-delete text-center">
        <i class="fa fa-trash"></i>
      </div>
    </div>
  </ng-template>
</ngx-datatable-column>

Does anyone know if there’s a better way to do this so that we could use the onActive method (bypassing the onSelect) ?

Intersting, but in case I put some button in column

 <ngx-datatable-column name="Registration Date"
                                prop="registrationDate"
                                [resizeable]="false"
                                [flexGrow]="1">
            <ng-template let-row="row" let-value="value" ngx-datatable-cell-template>
              <button
                  class="btn-primary btn"
                  (click)="emptyClickHandler()">Edit</button>
            </ng-template>
          </ngx-datatable-column>

So I have both (activate)="click()" as table component input and (click)="emptyClickHandler()" as button (click) input

there are no error.

Hello, I fixed using button. I changed my <i class=“ft-edit text-primary cursor-pointer” (click)=“open(content, ‘edit’)” > for <button class=“ft-edit text-primary cursor-pointer” (click)=“open(content, ‘edit’)”></button>

@xyrintech you could wrap ngx-datatables in an ng-container and use an *ngIf="...; let ..." over your stream. That’s what I did anyway. Works around the bug.

For the timebeing I wrapped the code at which this occurs in a setTimeout.

setTimeout(() => {
    //Code that triggers the error
})

I couldn’t get this to work @maxisam. Your solution relies on event bubbling which makes its reponse too late - the error has already happened. Shame because I thought it looked like a more elegant solution.