Ngx-datatable: ExpressionChangedAfterItHasBeenCheckedError

Created on 28 Apr 2017  ·  40Comments  ·  Source: swimlane/ngx-datatable

[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

Investigate

Most helpful comment

Same problem, resolved with blur...

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

All 40 comments

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.

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.

Hello, do we have any updates regarding this issue?

Exact same situation as @BotanMan . Trying to open BootstrapModal with dyn. component only that I'm using (active) not (select) to fire the modal. But exact same Error.

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'.

any news? @amcdnl ?

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?

Any updates to this issue i am getting the same error:
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'datatable-body-cell sort-active active'. Current value: 'datatable-body-cell sort-active'.

Is it related to https://github.com/angular/angular/issues/17572 by any chance?

I am still facing the issue. If i got a solution i comment on git hub in
this topic

On Wed, Aug 2, 2017 at 3:47 PM, Max notifications@github.com wrote:

Is it related to angular/angular#17572
https://github.com/angular/angular/issues/17572 by any chance?


You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/swimlane/ngx-datatable/issues/721#issuecomment-319631345,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AO5u-SfZkasULbqWGBE_N8mGFySLstN2ks5sUEyygaJpZM4NLbKd
.

--
Thanks & Regards

[image: Oodles Technologies Pvt Ltd] http://www.oodlestechnologies.com/

Himanshu Khurana Assistant Consultant-UI, Oodles Technologies Pvt Ltd
Phone: +91 124 4368 395
Mobile: +91 9899442240
Email: himanshu.[email protected]
maneesh@oodlestechnologies.com
Website: http://www.oodlestechnologies.com/
Skype: himanshukhurana.oodles

[image: Twitter] https://twitter.com/oodlestech [image: Facebook]
https://www.facebook.com/OodlesTech [image: Google +]
https://plus.google.com/+Oodlestechnologies/posts [image: LinkedIn]
https://www.linkedin.com/company/oodles-technologies-pvt-ltd

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

}

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

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.

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

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

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) ?

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

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.

Call event.cellElement.blur(); when click worked for me 👍

I am reporting that I am also experiencing this issue.

using I have cells that sometimes have a button that opens a model using ngBootstrap to show some data and this change detection error occurs each time the modal opens.

@maxisam - I was able to clear the error with your suggestion, thank you! Hopefully there will be some fix for this in the core library and I can remove this hack or find a better way around this without having to subscribe to activate(), as I do not need it for any other prupose

Error happens for us, it indicates that ngx-datatable is violating Angular's one way data flow. In development mode and non-AOT production builds luckily the only bad effect is the warning. With an AOT compiled build you get data synchronocity issues 😒

@maxtacco is this event only work for active? why the click event doesn't have the cellElement property?

@needforspeed I needed that for double click, but as @maxisam showed it should work for single click as well.

Anyone managed to fix this?

No solution seems to be working.

@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.

I understand, may I have the sample code for this?

<ng-container *ngIf="stream$ | async; let value"> then use the resolved value instead of stream$. No empty stream no troubles.

Any updates on this? We are running into the same issue...

I encountered this same issue today

I also encounter this problem today. I had to implement the workaround as others have mentioned above.
My template:
<ngx-datatable-column name="Job ID" 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:
public getTranLog(jobID: string, event:any) { event.target.parentElement.parentElement.blur(); this.modalRef = this._modalSvc.open(TranLogModalComponent); this.modalRef.componentInstance.jobID = jobID; }

Same problem, resolved with blur...

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

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.

Hoorayyy!

i fixed this error, added tag button for div

button.action__group-delete.action__group-btn(
   [inlineSVG]="'/assets/components/collections/archive/delete.svg'",
   (click)="delete(row.id)"
)
.action__group-delete.action__group-btn(
   [inlineSVG]="'/assets/components/collections/archive/delete.svg'",
   (click)="delete(row.id)"
)

without button we haved error on console

Previous value: 'className: datatable-body-cell archived__date sort-active active'. Current value: 'className: datatable-body-cell archived__date sort-active'

Use (mousedown) instead of (click) event. It's worked for me. It's not a proper solution but It's a workaround for now.

I ran into this same issue and was able to solve by using a button element instead of a link element. Links seems to work fine when routing to another page but if staying on the same page I've found the need to use buttons.

  <a [routerLink]="[ './', value ]" class="pl-3">
    <span class="fa fa-pencil">
    </span>
  </a>
  <!-- must be a button or throws ExpressionChangedAfterItHasBeenCheckedError! -->
  <button type="button" class="pl-3" (click)="delete(value);">
    <span class="fa fa-trash"></span>
  </button>

(update: fixed code formatting)

I faced same problem today. I tried all the of the recommend solutions. The one that worked and most easy to implement was solution suggested by @jessicahur .Thanks jessi for the easy pick up solution. This is how i implemented in my project.

In html component,

In ts component,

viewCityDetail(event, seletedCity) {
console.log(event, seletedCity)
event.target.parentElement.parentElement.blur();
const modalRef = this.modalService.open(CityDetailComponent, { size: 'lg' });
modalRef.componentInstance.user = { seletedCity: seletedCity };

}

Not sure if it's a cleaner solution, but it definitely works:

import { ɵmarkDirty as markDirty } from '@angular/core';

// and, at the very end of your event handler fn:
markDirty(this);

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

its work

Hello, I fixed using button. I changed my

for

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?

event.target.closest('datatable-body-cell').blur();
Add this line on your function

I solved the problem by changing the code


To :

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Csirme picture Csirme  ·  3Comments

id1945 picture id1945  ·  3Comments

Matthi0uw picture Matthi0uw  ·  3Comments

dinvlad picture dinvlad  ·  3Comments

TakhirMamirov picture TakhirMamirov  ·  3Comments