Skip to content

Commit 0875b85

Browse files
andrewseguinkara
authored andcommitted
feat(table): add row when predicate (#6795)
1 parent 36da4c3 commit 0875b85

File tree

10 files changed

+415
-50
lines changed

10 files changed

+415
-50
lines changed

src/cdk/table/row.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,22 @@ export class CdkHeaderRowDef extends BaseRowDef {
7575

7676
/**
7777
* Data row definition for the CDK table.
78-
* Captures the header row's template and other row properties such as the columns to display.
78+
* Captures the header row's template and other row properties such as the columns to display and
79+
* a when predicate that describes when this row should be used.
7980
*/
8081
@Directive({
8182
selector: '[cdkRowDef]',
82-
inputs: ['columns: cdkRowDefColumns'],
83+
inputs: ['columns: cdkRowDefColumns', 'when: cdkRowDefWhen'],
8384
})
84-
export class CdkRowDef extends BaseRowDef {
85+
export class CdkRowDef<T> extends BaseRowDef {
86+
/**
87+
* Function that should return true if this row template should be used for the provided row data
88+
* and index. If left undefined, this row will be considered the default row template to use when
89+
* no other when functions return true for the data.
90+
* For every row, there must be at least one when function that passes or an undefined to default.
91+
*/
92+
when: (rowData: T, index: number) => boolean;
93+
8594
// TODO(andrewseguin): Add an input for providing a switch function to determine
8695
// if this template should be used.
8796
constructor(template: TemplateRef<any>, _differs: IterableDiffers) {

src/cdk/table/table-errors.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,19 @@ export function getTableUnknownColumnError(id: string) {
2222
export function getTableDuplicateColumnNameError(name: string) {
2323
return Error(`cdk-table: Duplicate column definition name provided: "${name}".`);
2424
}
25+
26+
/**
27+
* Returns an error to be thrown when there are multiple rows that are missing a when function.
28+
* @docs-private
29+
*/
30+
export function getTableMultipleDefaultRowDefsError() {
31+
return Error(`cdk-table: There can only be one default row without a when predicate function.`);
32+
}
33+
34+
/**
35+
* Returns an error to be thrown when there are no matching row defs for a particular set of data.
36+
* @docs-private
37+
*/
38+
export function getTableMissingMatchingRowDefError() {
39+
return Error(`cdk-table: Could not find a matching row definition for the provided row data.`);
40+
}

src/cdk/table/table.spec.ts

Lines changed: 170 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@ import {Observable} from 'rxjs/Observable';
77
import {combineLatest} from 'rxjs/observable/combineLatest';
88
import {CdkTableModule} from './index';
99
import {map} from 'rxjs/operator/map';
10-
import {getTableDuplicateColumnNameError, getTableUnknownColumnError} from './table-errors';
10+
import {
11+
getTableDuplicateColumnNameError,
12+
getTableMissingMatchingRowDefError,
13+
getTableMultipleDefaultRowDefsError,
14+
getTableUnknownColumnError
15+
} from './table-errors';
1116

1217
describe('CdkTable', () => {
1318
let fixture: ComponentFixture<SimpleCdkTableApp>;
@@ -31,6 +36,9 @@ describe('CdkTable', () => {
3136
MissingColumnDefCdkTableApp,
3237
CrazyColumnNameCdkTableApp,
3338
UndefinedColumnsCdkTableApp,
39+
WhenRowCdkTableApp,
40+
WhenRowWithoutDefaultCdkTableApp,
41+
WhenRowMultipleDefaultsCdkTableApp
3442
],
3543
}).compileComponents();
3644
}));
@@ -202,6 +210,34 @@ describe('CdkTable', () => {
202210
});
203211
});
204212

213+
describe('using when predicate', () => {
214+
it('should be able to display different row templates based on the row data', () => {
215+
let whenFixture = TestBed.createComponent(WhenRowCdkTableApp);
216+
whenFixture.detectChanges();
217+
218+
let data = whenFixture.componentInstance.dataSource.data;
219+
expectTableToMatchContent(whenFixture.nativeElement.querySelector('cdk-table'), [
220+
['Column A', 'Column B', 'Column C'],
221+
[data[0].a, data[0].b, data[0].c],
222+
['index_1_special_row'],
223+
['c3_special_row'],
224+
[data[3].a, data[3].b, data[3].c],
225+
]);
226+
});
227+
228+
it('should error if there is row data that does not have a matching row template', () => {
229+
let whenFixture = TestBed.createComponent(WhenRowWithoutDefaultCdkTableApp);
230+
expect(() => whenFixture.detectChanges())
231+
.toThrowError(getTableMissingMatchingRowDefError().message);
232+
});
233+
234+
it('should error if there are multiple rows that do not have a when function', () => {
235+
let whenFixture = TestBed.createComponent(WhenRowMultipleDefaultsCdkTableApp);
236+
expect(() => whenFixture.detectChanges())
237+
.toThrowError(getTableMultipleDefaultRowDefsError().message);
238+
});
239+
});
240+
205241
it('should use differ to add/remove/move rows', () => {
206242
// Each row receives an attribute 'initialIndex' the element's original place
207243
getRows(tableElement).forEach((row: Element, index: number) => {
@@ -615,6 +651,139 @@ class SimpleCdkTableApp {
615651
@ViewChild(CdkTable) table: CdkTable<TestData>;
616652
}
617653

654+
@Component({
655+
template: `
656+
<cdk-table [dataSource]="dataSource">
657+
<ng-container cdkColumnDef="column_a">
658+
<cdk-header-cell *cdkHeaderCellDef> Column A</cdk-header-cell>
659+
<cdk-cell *cdkCellDef="let row"> {{row.a}}</cdk-cell>
660+
</ng-container>
661+
662+
<ng-container cdkColumnDef="column_b">
663+
<cdk-header-cell *cdkHeaderCellDef> Column B</cdk-header-cell>
664+
<cdk-cell *cdkCellDef="let row"> {{row.b}}</cdk-cell>
665+
</ng-container>
666+
667+
<ng-container cdkColumnDef="column_c">
668+
<cdk-header-cell *cdkHeaderCellDef> Column C</cdk-header-cell>
669+
<cdk-cell *cdkCellDef="let row"> {{row.c}}</cdk-cell>
670+
</ng-container>
671+
672+
<ng-container cdkColumnDef="index1Column">
673+
<cdk-header-cell *cdkHeaderCellDef> Column C</cdk-header-cell>
674+
<cdk-cell *cdkCellDef="let row"> index_1_special_row </cdk-cell>
675+
</ng-container>
676+
677+
<ng-container cdkColumnDef="c3Column">
678+
<cdk-header-cell *cdkHeaderCellDef> Column C</cdk-header-cell>
679+
<cdk-cell *cdkCellDef="let row"> c3_special_row </cdk-cell>
680+
</ng-container>
681+
682+
<cdk-header-row *cdkHeaderRowDef="columnsToRender"></cdk-header-row>
683+
<cdk-row *cdkRowDef="let row; columns: columnsToRender"></cdk-row>
684+
<cdk-row *cdkRowDef="let row; columns: ['index1Column']; when: isIndex1"></cdk-row>
685+
<cdk-row *cdkRowDef="let row; columns: ['c3Column']; when: hasC3"></cdk-row>
686+
</cdk-table>
687+
`
688+
})
689+
class WhenRowCdkTableApp {
690+
dataSource: FakeDataSource = new FakeDataSource();
691+
columnsToRender = ['column_a', 'column_b', 'column_c'];
692+
isIndex1 = (_rowData: TestData, index: number) => index == 1;
693+
hasC3 = (rowData: TestData) => rowData.c == 'c_3';
694+
695+
constructor() { this.dataSource.addData(); }
696+
697+
@ViewChild(CdkTable) table: CdkTable<TestData>;
698+
}
699+
700+
@Component({
701+
template: `
702+
<cdk-table [dataSource]="dataSource">
703+
<ng-container cdkColumnDef="column_a">
704+
<cdk-header-cell *cdkHeaderCellDef> Column A</cdk-header-cell>
705+
<cdk-cell *cdkCellDef="let row"> {{row.a}}</cdk-cell>
706+
</ng-container>
707+
708+
<ng-container cdkColumnDef="column_b">
709+
<cdk-header-cell *cdkHeaderCellDef> Column B</cdk-header-cell>
710+
<cdk-cell *cdkCellDef="let row"> {{row.b}}</cdk-cell>
711+
</ng-container>
712+
713+
<ng-container cdkColumnDef="column_c">
714+
<cdk-header-cell *cdkHeaderCellDef> Column C</cdk-header-cell>
715+
<cdk-cell *cdkCellDef="let row"> {{row.c}}</cdk-cell>
716+
</ng-container>
717+
718+
<ng-container cdkColumnDef="index1Column">
719+
<cdk-header-cell *cdkHeaderCellDef> Column C</cdk-header-cell>
720+
<cdk-cell *cdkCellDef="let row"> index_1_special_row </cdk-cell>
721+
</ng-container>
722+
723+
<ng-container cdkColumnDef="c3Column">
724+
<cdk-header-cell *cdkHeaderCellDef> Column C</cdk-header-cell>
725+
<cdk-cell *cdkCellDef="let row"> c3_special_row </cdk-cell>
726+
</ng-container>
727+
728+
<cdk-header-row *cdkHeaderRowDef="columnsToRender"></cdk-header-row>
729+
<cdk-row *cdkRowDef="let row; columns: ['index1Column']; when: isIndex1"></cdk-row>
730+
<cdk-row *cdkRowDef="let row; columns: ['c3Column']; when: hasC3"></cdk-row>
731+
</cdk-table>
732+
`
733+
})
734+
class WhenRowWithoutDefaultCdkTableApp {
735+
dataSource: FakeDataSource = new FakeDataSource();
736+
columnsToRender = ['column_a', 'column_b', 'column_c'];
737+
isIndex1 = (_rowData: TestData, index: number) => index == 1;
738+
hasC3 = (rowData: TestData) => rowData.c == 'c_3';
739+
740+
@ViewChild(CdkTable) table: CdkTable<TestData>;
741+
}
742+
743+
@Component({
744+
template: `
745+
<cdk-table [dataSource]="dataSource">
746+
<ng-container cdkColumnDef="column_a">
747+
<cdk-header-cell *cdkHeaderCellDef> Column A</cdk-header-cell>
748+
<cdk-cell *cdkCellDef="let row"> {{row.a}}</cdk-cell>
749+
</ng-container>
750+
751+
<ng-container cdkColumnDef="column_b">
752+
<cdk-header-cell *cdkHeaderCellDef> Column B</cdk-header-cell>
753+
<cdk-cell *cdkCellDef="let row"> {{row.b}}</cdk-cell>
754+
</ng-container>
755+
756+
<ng-container cdkColumnDef="column_c">
757+
<cdk-header-cell *cdkHeaderCellDef> Column C</cdk-header-cell>
758+
<cdk-cell *cdkCellDef="let row"> {{row.c}}</cdk-cell>
759+
</ng-container>
760+
761+
<ng-container cdkColumnDef="index1Column">
762+
<cdk-header-cell *cdkHeaderCellDef> Column C</cdk-header-cell>
763+
<cdk-cell *cdkCellDef="let row"> index_1_special_row </cdk-cell>
764+
</ng-container>
765+
766+
<ng-container cdkColumnDef="c3Column">
767+
<cdk-header-cell *cdkHeaderCellDef> Column C</cdk-header-cell>
768+
<cdk-cell *cdkCellDef="let row"> c3_special_row </cdk-cell>
769+
</ng-container>
770+
771+
<cdk-header-row *cdkHeaderRowDef="columnsToRender"></cdk-header-row>
772+
<cdk-row *cdkRowDef="let row; columns: columnsToRender"></cdk-row>
773+
<cdk-row *cdkRowDef="let row; columns: ['index1Column']"></cdk-row>
774+
<cdk-row *cdkRowDef="let row; columns: ['c3Column']; when: hasC3"></cdk-row>
775+
</cdk-table>
776+
`
777+
})
778+
class WhenRowMultipleDefaultsCdkTableApp {
779+
dataSource: FakeDataSource = new FakeDataSource();
780+
columnsToRender = ['column_a', 'column_b', 'column_c'];
781+
isIndex1 = (_rowData: TestData, index: number) => index == 1;
782+
hasC3 = (rowData: TestData) => rowData.c == 'c_3';
783+
784+
@ViewChild(CdkTable) table: CdkTable<TestData>;
785+
}
786+
618787
@Component({
619788
template: `
620789
<cdk-table [dataSource]="dataSource">

0 commit comments

Comments
 (0)