-
Notifications
You must be signed in to change notification settings - Fork 6.8k
feat(table): add optional footer row #10330
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
Conversation
src/cdk/table/cell.ts
Outdated
@@ -8,12 +8,17 @@ | |||
|
|||
import {ContentChild, Directive, ElementRef, Input, TemplateRef} from '@angular/core'; | |||
|
|||
/** Base class for a cell definition. Captures a column's cell template definition. */ | |||
export interface BaseCellDef { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typically you don't use "Base" for interfaces. Just CellDef
or CellDefinition
?
src/cdk/table/cell.ts
Outdated
}) | ||
export class CdkFooterCell { | ||
constructor(columnDef: CdkColumnDef, elementRef: ElementRef) { | ||
elementRef.nativeElement.classList.add(`cdk-column-${columnDef.cssClassFriendlyName}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Create a base class with the constructor so that all three directives don't duplicate it? Similar for the mat
versions
src/cdk/table/table.md
Outdated
the column a name. Each column definition then further defines both a header-cell template | ||
(`cdkHeaderCellDef`) and a data-cell template (`cdkCellDef`). | ||
the column a name. Each column definition then further defines a header-cell template | ||
(`cdkHeaderCellDef`), data-cell template (`cdkCellDef`), and footer-cell |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"...and an optional footer-cell template"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comment below
src/cdk/table/table.md
Outdated
the column a name. Each column definition then further defines a header-cell template | ||
(`cdkHeaderCellDef`), data-cell template (`cdkCellDef`), and footer-cell | ||
template (`cdkFooterCellDef`). Note that each of these cell definitions need to be present if they | ||
are used by their respective row definition. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that each of these cell definitions need to be present if they are used by their respective row definition.
It's not clear to me what this means
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the sentence and clarified the previous one a bit.
Each column definition can contain a header-cell template
(cdkHeaderCellDef
), data-cell template (cdkCellDef
), and footer-cell
template (cdkFooterCellDef
).
Technically they are all optional, but it depends on which row def uses it. E.g. if a header row def uses the column, it assumes a header cell def is present in the column
src/cdk/table/table.ts
Outdated
* @docs-private | ||
*/ | ||
@Directive({selector: '[footerRowPlaceholder]'}) | ||
export class FooterRowPlaceholder { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change "Placeholder" to "Outlet" in this PR since we're adding a new one?
src/cdk/table/table.ts
Outdated
@@ -214,6 +238,14 @@ export class CdkTable<T> implements CollectionViewer, OnInit, AfterContentChecke | |||
*/ | |||
@ContentChild(CdkHeaderRowDef) _headerRowDef: CdkHeaderRowDef; | |||
|
|||
/** | |||
* Template definition used as the header container. By default it stores the footer row |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"header" -> "footer"
// Make sure that the user has at least added a header row or row def. | ||
if (!this._headerRowDef && !this._rowDefs.length) { | ||
// Make sure that the user has at least added header, footer, or data row def. | ||
if (!this._headerRowDef && !this._footerRowDef && !this._rowDefs.length) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the presence of a footer should negate the need for a header or rows.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently the only requirement is that the table must have defined either amat-header-row
, mat-row
. If you do not have either, then an error is thrown. If you do not have one of them, they just are not rendered.
This continues the check - if you do not have any rows defined, an error is thrown. Otherwise, anything excluded just is not rendered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So you would say it's valid to have a footer row template defined with no header and no data row templates?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes mostly for symmetry. I've seen requests for tables that have only a header row and some requests to hide the header row. Doing the same treatment for footer
Some context: #7604
src/cdk/table/table.ts
Outdated
* of where to place the new row template in the placeholder. | ||
*/ | ||
private _renderRow( | ||
placeholder: any, rowDef: BaseRowDef, context: any = {}, index = 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the context not CdkCellOutletRowContext
?
mat-table { | ||
height: 500px; | ||
overflow: auto; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a demo-
class around these?
You should sync your client as well |
Synced |
Ready for review |
src/cdk/table/table.ts
Outdated
* of where to place the new row template in the outlet. | ||
*/ | ||
private _renderRow( | ||
outlet: any, rowDef: BaseRowDef, context: CdkCellOutletRowContext<T> = {}, index = 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
outlet
should be able to be more specific than any
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done - added a base class for the row outlets
3fda997
to
a749798
Compare
Will need to be rebased after the native |
a749798
to
98e2e45
Compare
Rebased |
@@ -0,0 +1,16 @@ | |||
<h3>Table with Footer</h3> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you want to change this one to a native table?
src/cdk/table/table.ts
Outdated
@@ -43,21 +50,34 @@ import { | |||
getTableUnknownDataSourceError | |||
} from './table-errors'; | |||
|
|||
export interface RowOutlet { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add description
@@ -66,8 +86,9 @@ export class HeaderRowPlaceholder { | |||
* material library. | |||
*/ | |||
export const CDK_TABLE_TEMPLATE = ` | |||
<ng-container headerRowPlaceholder></ng-container> | |||
<ng-container rowPlaceholder></ng-container>`; | |||
<ng-container headerRowOutlet></ng-container> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just noticed this, but this constant should be @docs-private
src/cdk/table/table.ts
Outdated
// header row def change so that the content check will render the header row. | ||
if (this._footerRowDef) { | ||
this._footerRowDefChanged = true; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this check (and the header one above) be replaced with
this._footerRowDefChanged = !!this._footerRowDef;
Or should it not be always set to false when _footerRowDef
is undefined?
src/cdk/table/table.ts
Outdated
|
||
this._getCellTemplatesForRow(row).forEach(cell => { | ||
this._getCellTemplates(rowDef).forEach(cellTemplate => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was like this before, but this would probably be better as a for of
just to avoid the extra function invocations in the critical path.
src/cdk/table/table.ts
Outdated
tbody.appendChild(this._rowPlaceholder.elementRef.nativeElement); | ||
thead.appendChild(this._headerRowOutlet.elementRef.nativeElement); | ||
tbody.appendChild(this._rowOutlet.elementRef.nativeElement); | ||
tfoot.appendChild(this._footerRowOutlet.elementRef.nativeElement); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can avoid some repetition:
const sections = [
{tag: 'thead', outlet: this._headerRowOutlet},
{tag: 'tbody', outlet: this._rowOutlet},
{tag: 'tfoot', outlet: this._footerRowOutlet},
];
for (const section of sections) {
const element = document.createElement(section.tag);
element.appendChild(section.outlet.elementRef.nativeElement);
this._elementRef.nativeElement.appendChild(element);
}
Cool thanks for the review -- good suggestions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
3a194d1
to
b121be8
Compare
@andrewseguin needs rebase, re-apply |
b121be8
to
6dda130
Compare
6dda130
to
84642b2
Compare
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Adds an optional footer row to the table. Since header row has similar logic, a refactoring of the table code was necessary to avoid duplicating work.
Note that the table.spec.ts was reworked as well to remove a lot of boilerplate (most tests begin by setting up a fixture, component, table element, etc.). Added a utility function at the top of the file to handle this.
Closes #7548