Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/cdk/table/table.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,25 @@ describe('CdkTable', () => {
]);
});

it('should replace only the row whose row definition changed', () => {
setupTableTestApp(WhenRowChangeDetectionCdkTableApp);
const initialRows = getRows(tableElement);

initialRows.forEach(row => expect(row.classList).toContain('default-row'));

component.showAlternate = true;
component.table.renderRows();
fixture.detectChanges();

const updatedRows = getRows(tableElement);
expect(updatedRows[0]).toBe(initialRows[0]);
expect(updatedRows[1]).not.toBe(initialRows[1]);
expect(updatedRows[2]).toBe(initialRows[2]);
expect(updatedRows[0].classList).toContain('default-row');
expect(updatedRows[1].classList).toContain('alternate-row');
expect(updatedRows[2].classList).toContain('default-row');
});

it('should error if there is row data that does not have a matching row template', fakeAsync(() => {
const whenRowWithoutDefaultFixture = TestBed.createComponent(
WhenRowWithoutDefaultCdkTableApp,
Expand Down Expand Up @@ -2405,6 +2424,31 @@ class WhenRowCdkTableApp {
}
}

@Component({
template: `
<cdk-table [dataSource]="dataSource" [trackBy]="trackByIndex">
<ng-container cdkColumnDef="column_a">
<cdk-cell *cdkCellDef="let row"> {{row.a}} </cdk-cell>
</ng-container>

<cdk-row *cdkRowDef="let row; columns: ['column_a']" class="default-row"></cdk-row>
<cdk-row *cdkRowDef="let row; columns: ['column_a']; when: isAlternateRow"
class="alternate-row"></cdk-row>
</cdk-table>
`,
imports: [CdkTableModule],
changeDetection: ChangeDetectionStrategy.Eager,
})
class WhenRowChangeDetectionCdkTableApp {
dataSource = new FakeDataSource();
showAlternate = false;

@ViewChild(CdkTable) table!: CdkTable<TestData>;

trackByIndex = (index: number, _item: TestData) => index;
isAlternateRow = (index: number, _item: TestData) => this.showAlternate && index === 1;
}

@Component({
template: `
<cdk-table [dataSource]="dataSource" multiTemplateDataRows>
Expand Down
28 changes: 23 additions & 5 deletions src/cdk/table/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,12 @@ export class CdkTable<T>
*/
private _cachedRenderRowsMap = new Map<T, WeakMap<CdkRowDef<T>, RenderRow<T>[]>>();

/**
* Row definition used to create each rendered data-row view. Keying by the view keeps the
* association intact when the differ moves rows.
*/
private _rowDefsByView = new WeakMap<EmbeddedViewRef<RowContext<T>>, CdkRowDef<T>>();

/** Whether the table is applied to a native `<table>`. */
protected _isNativeHtmlTable: boolean;

Expand Down Expand Up @@ -750,20 +756,32 @@ export class CdkTable<T>
(change: _ViewRepeaterItemChange<RenderRow<T>, RowContext<T>>) => {
if (change.operation === _ViewRepeaterOperation.INSERTED && change.context) {
this._renderCellTemplateForItem(change.record.item.rowDef, change.context);
const rowView = viewContainer.get(change.record.currentIndex!) as RowViewRef<T>;
this._rowDefsByView.set(rowView, change.record.item.rowDef);
}
},
);

// Update the meta context of a row's context data (index, count, first, last, ...)
this._updateRowIndexContext();

// Update rows that did not get added/removed/moved but may have had their identity changed,
// e.g. if trackBy matched data on some property but the actual data reference changed.
changes.forEachIdentityChange((record: IterableChangeRecord<RenderRow<T>>) => {
const rowView = <RowViewRef<T>>viewContainer.get(record.currentIndex!);
rowView.context.$implicit = record.item.data;
const currentIndex = record.currentIndex!;
const rowView = viewContainer.get(currentIndex) as RowViewRef<T>;

if (this._rowDefsByView.get(rowView) !== record.item.rowDef) {
viewContainer.remove(currentIndex);
const newRowView = this._renderRow(this._rowOutlet, record.item.rowDef, currentIndex, {
$implicit: record.item.data,
});
this._rowDefsByView.set(newRowView, record.item.rowDef);
} else {
rowView.context.$implicit = record.item.data;
}
});

// Update the meta context of a row's context data (index, count, first, last, ...)
this._updateRowIndexContext();

this._updateNoDataRow();

this.contentChanged.next();
Expand Down