Scott B
Scott B

Reputation: 40157

Angular Material MatTable appears to reload data on previously rendered rows?

I am using the MatTable in which the first row is pre-populated with a MatAutocomplete input control that is bound to a dynamic data call. Everything works as expected there.

I have an "Add Row" button that calls a method to update the table with a new row. The issue I'm trying to resolve or better understand is that it appears that when the new row is added, all the previous rows get reloaded even though their data has not changed.

I'm using onPush changedetection. Anyone know if its possible to optimize the matTable to not rerender previously rendered rows?

addRow(event: any) {
    this.clearSort();
    this.clearFilter(undefined);
    const currentData = cloneDeep(this.formArrayValues);
    currentData.push(this.emptyRowFromColumnDef); // Append new row to END of table
    this.updateTableData(currentData);
    if (this.paginationEnable && this.paginator) {
      // move table paging to last page to see newly added row. setTimeout helps avoid sync delay
      timer().subscribe(() => this.paginator.lastPage());
    }
    timer(1).subscribe(() => this.changeDetectorRef.markForCheck());
    event.preventDefault();
  }


get emptyRowFromColumnDef(): any {
  const item = {};
  const columns = get(this.tableConfig, `columns`, []);
  const columnTypes = get(this.tableConfig, `columnTypes`, {});
  if (columns.length) {
    for (let i = 0; i < columns.length; i++) {
      if (columns[i] === this.SELECTION_COLUMN_NAME) {
        item[columns[i]] = true;
      } else {
        // Todo - Set this to the correct default type. Should be able to use tableConfig
        item[columns[i]] = this.defaultEmptyValueByType(columns[i], columnTypes[columns[i]]);
      }
    }
  }

  return item;
}

get formArrayValues(): any[] {
  return this.formArray.getRawValue();
}

get formArray(): FormArray {
  return <FormArray>this.formControl;
}


updateTableData(datasource: any) {
  this.resetFilterIfActive();
  const useData = this.defaultEmptyColumnData(this._limitDataByMode(datasource));
  this.data = useData;
  if (this.paginationEnable) {
    this.paginatorLength = useData.length || 0;
  }
  this.syncDatasourceToFormControl();
  this.renderRows();
}


syncDatasourceToFormControl() {
  if (!['builder'].includes(this.jsf.mode)) {

    if (this.formArray && isFunction(this.formArray.clear)) {
      this.formArray.clear();
    }

    let data = this.data || [];
    this.formArrayUpdateAll();

    if (this.options.rowSelection) {
      data = this.defaultUndefinedSelection(data);
    }

    if (this.formArray) {
      this.bindDataToForm(data);
      this.renderRows();
      this.testLogicSyncChanges();
    }
  }
}


formArrayUpdateAll() {
    const dataTotal = (this.data || []).length;
    const $refNode = this._getRefNode();
    if ($refNode) {
      $refNode.options.tableItem = true;
      for (let i = 0; i < dataTotal; i++) {
        const index = i + 1;
        const $ref = {
          layoutNode: $refNode,
          layoutIndex: this.layoutIndex.concat(index),
          dataIndex: this.dataIndex.concat(index)
        };
        this.jsf.addItem($ref);
      }
    }
  }

Upvotes: 0

Views: 441

Answers (1)

Steven Vaught
Steven Vaught

Reputation: 36

I'm assuming by 'reloaded' you mean 'rerendered'. You might be able to use trackBy. From material angular docs:

To improve performance, a trackBy function can be provided to the table similar to Angular’s ngFor trackBy. This informs the table how to uniquely identify rows to track how the data changes with each update.

Upvotes: 1

Related Questions