Search code examples
angularchange-detector-ref

Why do I need mark for check ? angular


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.


Solution

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