Skip to content

Commit

Permalink
feat(table): support directly adding column, row, and header defs (#8744
Browse files Browse the repository at this point in the history
)

* feat(table): support added column and row defs

* fix imports

* fix imports again
  • Loading branch information
andrewseguin committed Dec 13, 2017
1 parent 4f78613 commit 693c8e8
Show file tree
Hide file tree
Showing 17 changed files with 608 additions and 44 deletions.
4 changes: 4 additions & 0 deletions src/cdk/table/cell.ts
Expand Up @@ -36,6 +36,10 @@ export class CdkColumnDef {
@Input('cdkColumnDef')
get name(): string { return this._name; }
set name(name: string) {
// If the directive is set without a name (updated programatically), then this setter will
// trigger with an empty string and should not overwrite the programatically set value.
if (!name) { return; }

this._name = name;
this.cssClassFriendlyName = name.replace(/[^a-z0-9_-]/ig, '-');
}
Expand Down
91 changes: 88 additions & 3 deletions src/cdk/table/table.spec.ts
@@ -1,5 +1,5 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {Component, ViewChild} from '@angular/core';
import {Component, ContentChild, ContentChildren, Input, QueryList, ViewChild} from '@angular/core';
import {CdkTable} from './table';
import {CollectionViewer, DataSource} from '@angular/cdk/collections';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
Expand All @@ -13,6 +13,8 @@ import {
getTableMultipleDefaultRowDefsError,
getTableUnknownColumnError
} from './table-errors';
import {CdkHeaderRowDef, CdkRowDef} from './row';
import {CdkColumnDef} from './cell';

describe('CdkTable', () => {
let fixture: ComponentFixture<SimpleCdkTableApp>;
Expand Down Expand Up @@ -40,7 +42,9 @@ describe('CdkTable', () => {
WhenRowWithoutDefaultCdkTableApp,
WhenRowMultipleDefaultsCdkTableApp,
MissingRowDefsCdkTableApp,
BooleanRowCdkTableApp
BooleanRowCdkTableApp,
WrapperCdkTableApp,
OuterTableApp,
],
}).compileComponents();
}));
Expand Down Expand Up @@ -182,7 +186,6 @@ describe('CdkTable', () => {
it('should be able to dynamically add/remove column definitions', () => {
const dynamicColumnDefFixture = TestBed.createComponent(DynamicColumnDefinitionsCdkTableApp);
dynamicColumnDefFixture.detectChanges();
dynamicColumnDefFixture.detectChanges();

const dynamicColumnDefTable = dynamicColumnDefFixture.nativeElement.querySelector('cdk-table');
const dynamicColumnDefComp = dynamicColumnDefFixture.componentInstance;
Expand Down Expand Up @@ -232,6 +235,22 @@ describe('CdkTable', () => {
});
});

it('should be able to register column, row, and header row definitions outside content', () => {
const outerTableAppFixture = TestBed.createComponent(OuterTableApp);
outerTableAppFixture.detectChanges();

// The first two columns were defined in the wrapped table component as content children,
// while the injected columns were provided to the wrapped table from the outer component.
// A special row was provided with a when predicate that shows the single column with text.
// The header row was defined by the outer component.
expectTableToMatchContent(outerTableAppFixture.nativeElement.querySelector('cdk-table'), [
['Content Column A', 'Content Column B', 'Injected Column A', 'Injected Column B'],
['injected row with when predicate'],
['a_2', 'b_2', 'a_2', 'b_2'],
['a_3', 'b_3', 'a_3', 'b_3']
]);
});

describe('using when predicate', () => {
it('should be able to display different row templates based on the row data', () => {
let whenFixture = TestBed.createComponent(WhenRowCdkTableApp);
Expand Down Expand Up @@ -1072,6 +1091,72 @@ class RowContextCdkTableApp {
enableCellContextClasses = false;
}

@Component({
selector: 'wrapper-table',
template: `
<cdk-table [dataSource]="dataSource">
<ng-container cdkColumnDef="content_column_a">
<cdk-header-cell *cdkHeaderCellDef> Content Column A </cdk-header-cell>
<cdk-cell *cdkCellDef="let row"> {{row.a}} </cdk-cell>
</ng-container>
<ng-container cdkColumnDef="content_column_b">
<cdk-header-cell *cdkHeaderCellDef> Content Column B </cdk-header-cell>
<cdk-cell *cdkCellDef="let row"> {{row.b}} </cdk-cell>
</ng-container>
<cdk-row *cdkRowDef="let row; columns: columns"></cdk-row>
</cdk-table>
`
})
class WrapperCdkTableApp<T> {
@ContentChildren(CdkColumnDef) columnDefs: QueryList<CdkColumnDef>;
@ContentChild(CdkHeaderRowDef) headerRowDef: CdkHeaderRowDef;
@ContentChildren(CdkRowDef) rowDefs: QueryList<CdkRowDef<T>>;

@ViewChild(CdkTable) table: CdkTable<T>;

@Input() columns: string[];
@Input() dataSource: DataSource<T>;

ngAfterContentInit() {
// Register the content's column, row, and header row definitions.
this.columnDefs.forEach(columnDef => this.table.addColumnDef(columnDef));
this.rowDefs.forEach(rowDef => this.table.addRowDef(rowDef));
this.table.setHeaderRowDef(this.headerRowDef);
}
}

@Component({
template: `
<wrapper-table [dataSource]="dataSource" [columns]="columnsToRender">
<ng-container cdkColumnDef="injected_column_a">
<cdk-header-cell *cdkHeaderCellDef> Injected Column A </cdk-header-cell>
<cdk-cell *cdkCellDef="let row"> {{row.a}} </cdk-cell>
</ng-container>
<ng-container cdkColumnDef="injected_column_b">
<cdk-header-cell *cdkHeaderCellDef> Injected Column B </cdk-header-cell>
<cdk-cell *cdkCellDef="let row"> {{row.b}} </cdk-cell>
</ng-container>
<!-- Only used for the 'when' row, the first row -->
<ng-container cdkColumnDef="special_column">
<cdk-cell *cdkCellDef="let row"> injected row with when predicate </cdk-cell>
</ng-container>
<cdk-header-row *cdkHeaderRowDef="columnsToRender"></cdk-header-row>
<cdk-row class="first-row" *cdkRowDef="let row; columns: ['special_column']; when: firstRow">
</cdk-row>
</wrapper-table>
`
})
class OuterTableApp {
dataSource: FakeDataSource = new FakeDataSource();
columnsToRender =
['content_column_a', 'content_column_b', 'injected_column_a', 'injected_column_b'];

firstRow = i => i === 0;
}

function getElements(element: Element, query: string): Element[] {
return [].slice.call(element.querySelectorAll(query));
}
Expand Down

0 comments on commit 693c8e8

Please sign in to comment.