Why do I need mark for check ? angular

1.2k views Asked by At

I am using angular 10, cdktable and I face the following problem.

when I execute a search on a table, my pagination do not update unless I click anywhere in the app (that it magically update)...

This is a change detection problem, my app is using onPush strategy.

now, my table is getting data from this observable

connect(): Observable < Advertisers_advertisers_rows[] > {
  return this.store.pipe(
    select(advertiserPage),
    map((res) => {
      this.itemTotal = res.data.records
      this.page = res.data.page
      return res?.data?.rows || []
    })
  )
}

note that here I am updating the total number of item and the page.

now, this is passed to the table component

table.component.ts

<app-table
  [dataSource]="this"
  [pagination]="paginationOptions"
  (pageChange)="loadPage($event)"
  [itemTotal]="itemTotal"
></app-table>

which call itself the pagination

pagination.ts:

<app-table-pagination
  *ngIf="pagination"
  [options]="pagination"
  [(page)]="page"
  (pageChange)="pageChange.emit($event)"
  [total]="itemTotal"
></app-table-pagination>

If I follow the logic, the itemTotal change, it is passed as input to table and to pagination so it should trigger a change.

Everything works if I do

connect(): Observable < Advertisers_advertisers_rows[] > {
  return this.store.pipe(
    select(advertiserPage),
    map((res) => {
      this.itemTotal = res.data.records
      this.page = res.data.page
      this.changeDetection.markForCheck()
      return res?.data?.rows || []
    })
  )
}

But I do not understand why is this necessary.

2

There are 2 answers

1
StepUp On BEST ANSWER

When you use OnPush strategy, then you need to use markForCheck() method to say Angular that view is changed and view should be updated.

As Angular docs says:

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

and:

Components are normally marked as dirty (in need of rerendering) when inputs have changed or events have fired in the view. Call this method to ensure that a component is checked even if these triggers have not occured.

UPDATE:

itemTotal is not updated because Angular is not informed about this change. How we can inform? We need to mark the path from our component until root to be checked for the next change detection run:

this.itemTotal = res.data.records
this.page = res.data.page
this.changeDetection.markForCheck()

You can read more here.

0
Indragith On

ChangeDetectionStrategy of your component is OnPush which means the change detection will run only when the references of the @inputs has been completely replaced with a new value.

For example:

var obj = {
    test:'test'
};

If you update the obj value as

obj.test = 'new'

angular will not run the change detection in the above case since the reference of the obj is same. As a result, view will not be updated. To update the view, manually we need to tell the angular to run change detection.

For this, use markForCheck or detectchanges.

In your case, Eventhough both the itemTotal and page has updated with new value but it points to the same reference. To update the view, markForCheck is needed.