Skip to content

feat(cdk/table): add API for registering no data row #20658

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 2, 2020
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
14 changes: 13 additions & 1 deletion src/cdk/table/table.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {BehaviorSubject, combineLatest, Observable, of as observableOf} from 'rx
import {map} from 'rxjs/operators';
import {CdkColumnDef} from './cell';
import {CdkTableModule} from './index';
import {CdkHeaderRowDef, CdkRowDef, CdkCellOutlet} from './row';
import {CdkHeaderRowDef, CdkRowDef, CdkCellOutlet, CdkNoDataRow} from './row';
import {CdkTable} from './table';
import {
getTableDuplicateColumnNameError,
Expand Down Expand Up @@ -703,6 +703,15 @@ describe('CdkTable', () => {
]);
});

it('should be able to register a no data row defined outside the table', () => {
setupTableTestApp(OuterTableApp, [WrapperCdkTableApp]);

fixture.componentInstance.dataSource.data = [];
fixture.detectChanges();

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

describe('using when predicate', () => {
it('should be able to display different row templates based on the row data', () => {
setupTableTestApp(WhenRowCdkTableApp);
Expand Down Expand Up @@ -2308,13 +2317,15 @@ class RowContextCdkTableApp {
</ng-container>

<cdk-row *cdkRowDef="let row; columns: columns"></cdk-row>
<ng-template cdkNoDataRow>No data</ng-template>
</cdk-table>
`
})
class WrapperCdkTableApp<T> implements AfterContentInit {
@ContentChildren(CdkColumnDef) columnDefs: QueryList<CdkColumnDef>;
@ContentChild(CdkHeaderRowDef) headerRowDef: CdkHeaderRowDef;
@ContentChildren(CdkRowDef) rowDefs: QueryList<CdkRowDef<T>>;
@ContentChild(CdkNoDataRow) noDataRow: CdkNoDataRow;

@ViewChild(CdkTable, {static: true}) table: CdkTable<T>;

Expand All @@ -2326,6 +2337,7 @@ class WrapperCdkTableApp<T> implements AfterContentInit {
this.columnDefs.forEach(columnDef => this.table.addColumnDef(columnDef));
this.rowDefs.forEach(rowDef => this.table.addRowDef(rowDef));
this.table.addHeaderRowDef(this.headerRowDef);
this.table.setNoDataRow(this.noDataRow);
}
}

Expand Down
14 changes: 12 additions & 2 deletions src/cdk/table/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,9 @@ export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDes
*/
private _customFooterRowDefs = new Set<CdkFooterRowDef>();

/** No data row that was defined outside of the direct content children of the table. */
private _customNoDataRow: CdkNoDataRow | null;

/**
* Whether the header row definition has been changed. Triggers an update to the header row after
* content is checked. Initialized as true so that the table renders the initial set of rows.
Expand Down Expand Up @@ -694,6 +697,11 @@ export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDes
this._footerRowDefChanged = true;
}

/** Sets a no data row definition that was not included as a part of the content children. */
setNoDataRow(noDataRow: CdkNoDataRow | null) {
this._customNoDataRow = noDataRow;
}

/**
* Updates the header sticky styles. First resets all applied styles with respect to the cells
* sticking to the top. Then, evaluating which cells need to be stuck to the top. This is
Expand Down Expand Up @@ -1228,12 +1236,14 @@ export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDes

/** Creates or removes the no data row, depending on whether any data is being shown. */
private _updateNoDataRow() {
if (this._noDataRow) {
const noDataRow = this._customNoDataRow || this._noDataRow;

if (noDataRow) {
const shouldShow = this._rowOutlet.viewContainer.length === 0;

if (shouldShow !== this._isShowingNoDataRow) {
const container = this._noDataRowOutlet.viewContainer;
shouldShow ? container.createEmbeddedView(this._noDataRow.templateRef) : container.clear();
shouldShow ? container.createEmbeddedView(noDataRow.templateRef) : container.clear();
this._isShowingNoDataRow = shouldShow;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
table {
width: 100%;
}

button {
margin: 0 8px 8px 0;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
<div>
<button mat-raised-button (click)="clearTable()">Clear table</button>
<button mat-raised-button (click)="addData()">Add data</button>
</div>

<wrapper-table [dataSource]="dataSource" [columns]="displayedColumns"
matSort #sort="matSort">
<!-- Custom column definition to be provided to the wrapper table. -->
Expand All @@ -8,5 +13,10 @@

<!-- Custom row definitions to be provided to the wrapper table. -->
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns; "></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>

<!-- Row shown when there is no matching data that will be provided to the wrapper table. -->
<tr class="mat-row" *matNoDataRow>
<td class="mat-cell" colspan="4">No data</td>
</tr>
</wrapper-table>
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import {
Input,
AfterViewInit,
QueryList,
ViewChild
ViewChild,
ContentChild,
} from '@angular/core';
import {MatSort} from '@angular/material/sort';
import {
MatColumnDef,
MatHeaderRowDef,
MatNoDataRow,
MatRowDef,
MatTable,
MatTableDataSource
Expand Down Expand Up @@ -54,6 +56,14 @@ export class TableWrappedExample implements AfterViewInit {
ngAfterViewInit() {
this.dataSource.sort = this.sort;
}

clearTable() {
this.dataSource.data = [];
}

addData() {
this.dataSource.data = ELEMENT_DATA;
}
}

/**
Expand All @@ -73,6 +83,7 @@ export class WrapperTable<T> implements AfterContentInit {
@ContentChildren(MatHeaderRowDef) headerRowDefs: QueryList<MatHeaderRowDef>;
@ContentChildren(MatRowDef) rowDefs: QueryList<MatRowDef<T>>;
@ContentChildren(MatColumnDef) columnDefs: QueryList<MatColumnDef>;
@ContentChild(MatNoDataRow) noDataRow: MatNoDataRow;

@ViewChild(MatTable, {static: true}) table: MatTable<T>;

Expand All @@ -84,5 +95,6 @@ export class WrapperTable<T> implements AfterContentInit {
this.columnDefs.forEach(columnDef => this.table.addColumnDef(columnDef));
this.rowDefs.forEach(rowDef => this.table.addRowDef(rowDef));
this.headerRowDefs.forEach(headerRowDef => this.table.addHeaderRowDef(headerRowDef));
this.table.setNoDataRow(this.noDataRow);
}
}
1 change: 1 addition & 0 deletions tools/public_api_guard/cdk/table.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ export declare class CdkTable<T> implements AfterContentChecked, CollectionViewe
removeHeaderRowDef(headerRowDef: CdkHeaderRowDef): void;
removeRowDef(rowDef: CdkRowDef<T>): void;
renderRows(): void;
setNoDataRow(noDataRow: CdkNoDataRow | null): void;
updateStickyColumnStyles(): void;
updateStickyFooterRowStyles(): void;
updateStickyHeaderRowStyles(): void;
Expand Down