Skip to content
Merged
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
31 changes: 23 additions & 8 deletions src/cdk/table/table.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ describe('CdkTable', () => {
it('with a connected data source', () => {
expect(table.dataSource).toBe(dataSource);
expect(dataSource.isConnected).toBe(true);
expect(component.contentChangedCount).toBe(1);
});

it('with a rendered header with the right number of header cells', () => {
Expand Down Expand Up @@ -135,6 +136,9 @@ describe('CdkTable', () => {
dataSource.addData();
fixture.detectChanges();

// Expect it to have emitted once on init, and once again for the data changes.
expect(component.contentChangedCount).toBe(2);

expect(getRows(tableElement).length).toBe(dataSource.data.length);

// Check that the number of cells is correct
Expand Down Expand Up @@ -303,6 +307,9 @@ describe('CdkTable', () => {
dataSource.data = originalData;
fixture.detectChanges();

// Expect it to have emitted once on init, once when empty, and again with original data.
expect(component.contentChangedCount).toBe(3);

expect(tableElement.textContent!.trim()).not.toContain('No data');
});

Expand All @@ -313,6 +320,7 @@ describe('CdkTable', () => {
fixture.detectChanges();
tableElement = fixture.nativeElement.querySelector('.cdk-table');
expect(tableElement.textContent!.trim()).toContain('No data');
expect(component.contentChangedCount).toBe(1);
});
});

Expand All @@ -321,6 +329,9 @@ describe('CdkTable', () => {
fixture.detectChanges();

expect(getRows(tableElement).length).toBe(0);

// Emits that the data rows are changed even when the result is empty.
expect(component.contentChangedCount).toBe(1);
}));

it('should be able to render multiple header and footer rows', () => {
Expand Down Expand Up @@ -1850,23 +1861,24 @@ class BooleanDataSource extends DataSource<boolean> {

@Component({
template: `
<cdk-table [dataSource]="dataSource">
<cdk-table [dataSource]="dataSource"
(contentChanged)="contentChangedCount = contentChangedCount + 1">
<ng-container cdkColumnDef="column_a">
<cdk-header-cell *cdkHeaderCellDef> Column A </cdk-header-cell>
<cdk-header-cell *cdkHeaderCellDef> Column A</cdk-header-cell>
<cdk-cell *cdkCellDef="let row"> {{row.a}} </cdk-cell>
<cdk-footer-cell *cdkFooterCellDef> Footer A </cdk-footer-cell>
<cdk-footer-cell *cdkFooterCellDef> Footer A</cdk-footer-cell>
</ng-container>

<ng-container cdkColumnDef="column_b">
<cdk-header-cell *cdkHeaderCellDef> Column B </cdk-header-cell>
<cdk-header-cell *cdkHeaderCellDef> Column B</cdk-header-cell>
<cdk-cell *cdkCellDef="let row"> {{row.b}} </cdk-cell>
<cdk-footer-cell *cdkFooterCellDef> Footer B </cdk-footer-cell>
<cdk-footer-cell *cdkFooterCellDef> Footer B</cdk-footer-cell>
</ng-container>

<ng-container cdkColumnDef="column_c">
<cdk-header-cell *cdkHeaderCellDef> Column C </cdk-header-cell>
<cdk-header-cell *cdkHeaderCellDef> Column C</cdk-header-cell>
<cdk-cell *cdkCellDef="let row"> {{row.c}} </cdk-cell>
<cdk-footer-cell *cdkFooterCellDef> Footer C </cdk-footer-cell>
<cdk-footer-cell *cdkFooterCellDef> Footer C</cdk-footer-cell>
</ng-container>

<cdk-header-row class="customHeaderRowClass"
Expand All @@ -1883,6 +1895,7 @@ class BooleanDataSource extends DataSource<boolean> {
class SimpleCdkTableApp {
dataSource: FakeDataSource | undefined = new FakeDataSource();
columnsToRender = ['column_a', 'column_b', 'column_c'];
contentChangedCount = 0;

@ViewChild(CdkTable) table: CdkTable<TestData>;
}
Expand Down Expand Up @@ -1937,7 +1950,8 @@ class BooleanRowCdkTableApp {

@Component({
template: `
<cdk-table [dataSource]="dataSource">
<cdk-table [dataSource]="dataSource"
(contentChanged)="contentChangedCount = contentChangedCount + 1">
<ng-container cdkColumnDef="column_a">
<cdk-header-cell *cdkHeaderCellDef></cdk-header-cell>
<cdk-cell *cdkCellDef="let data"> {{data}} </cdk-cell>
Expand All @@ -1950,6 +1964,7 @@ class BooleanRowCdkTableApp {
})
class NullDataCdkTableApp {
dataSource = observableOf(null);
contentChangedCount = 0;
}


Expand Down
12 changes: 12 additions & 0 deletions src/cdk/table/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
Directive,
ElementRef,
EmbeddedViewRef,
EventEmitter,
Inject,
Input,
IterableChangeRecord,
Expand All @@ -42,6 +43,7 @@ import {
OnDestroy,
OnInit,
Optional,
Output,
QueryList,
SkipSelf,
TemplateRef,
Expand Down Expand Up @@ -455,6 +457,13 @@ export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDes
}
private _fixedLayout: boolean = false;

/**
* Emits when the table completes rendering a set of data rows based on the latest data from the
* data source, even if the set of rows is empty.
*/
@Output()
readonly contentChanged = new EventEmitter<void>();

// TODO(andrewseguin): Remove max value as the end index
// and instead calculate the view on init and scroll.
/**
Expand Down Expand Up @@ -612,6 +621,7 @@ export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDes
const changes = this._dataDiffer.diff(this._renderRows);
if (!changes) {
this._updateNoDataRow();
this.contentChanged.next();
return;
}
const viewContainer = this._rowOutlet.viewContainer;
Expand Down Expand Up @@ -639,6 +649,8 @@ export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDes

this._updateNoDataRow();
this.updateStickyColumnStyles();

this.contentChanged.next();
}

/** Adds a column definition that was not included as part of the content children. */
Expand Down
3 changes: 2 additions & 1 deletion tools/public_api_guard/cdk/table.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ export declare class CdkTable<T> implements AfterContentChecked, CollectionViewe
_rowOutlet: DataRowOutlet;
protected readonly _stickyPositioningListener: StickyPositioningListener;
protected readonly _viewRepeater: _ViewRepeater<T, RenderRow<T>, RowContext<T>>;
readonly contentChanged: EventEmitter<void>;
get dataSource(): CdkTableDataSourceInput<T>;
set dataSource(dataSource: CdkTableDataSourceInput<T>);
get fixedLayout(): boolean;
Expand Down Expand Up @@ -247,7 +248,7 @@ export declare class CdkTable<T> implements AfterContentChecked, CollectionViewe
updateStickyHeaderRowStyles(): void;
static ngAcceptInputType_fixedLayout: BooleanInput;
static ngAcceptInputType_multiTemplateDataRows: BooleanInput;
static ɵcmp: i0.ɵɵComponentDeclaration<CdkTable<any>, "cdk-table, table[cdk-table]", ["cdkTable"], { "trackBy": "trackBy"; "dataSource": "dataSource"; "multiTemplateDataRows": "multiTemplateDataRows"; "fixedLayout": "fixedLayout"; }, {}, ["_noDataRow", "_contentColumnDefs", "_contentRowDefs", "_contentHeaderRowDefs", "_contentFooterRowDefs"], ["caption", "colgroup, col"]>;
static ɵcmp: i0.ɵɵComponentDeclaration<CdkTable<any>, "cdk-table, table[cdk-table]", ["cdkTable"], { "trackBy": "trackBy"; "dataSource": "dataSource"; "multiTemplateDataRows": "multiTemplateDataRows"; "fixedLayout": "fixedLayout"; }, { "contentChanged": "contentChanged"; }, ["_noDataRow", "_contentColumnDefs", "_contentRowDefs", "_contentHeaderRowDefs", "_contentFooterRowDefs"], ["caption", "colgroup, col"]>;
static ɵfac: i0.ɵɵFactoryDeclaration<CdkTable<any>, [null, null, null, { attribute: "role"; }, { optional: true; }, null, null, null, null, null, { optional: true; skipSelf: true; }]>;
}

Expand Down