mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(table): errors when rendering table with sticky elements on the server (#12095)
Fixes a handful of errors that were being thrown by `mat-table` when it has sticky rows and when it's being used with the native `table` elements. Fixes #12094.
This commit is contained in:
committed by
Jeremy Elbourn
parent
6a1a70753e
commit
7e67fe936c
@@ -11,6 +11,7 @@ ng_module(
|
||||
"//src/cdk/bidi",
|
||||
"//src/cdk/collections",
|
||||
"//src/cdk/coercion",
|
||||
"//src/cdk/platform",
|
||||
"@rxjs",
|
||||
],
|
||||
tsconfig = "//src/cdk:tsconfig-build.json",
|
||||
|
||||
@@ -32,10 +32,12 @@ export class StickyStyler {
|
||||
* sticky positioning applied.
|
||||
* @param direction The directionality context of the table (ltr/rtl); affects column positioning
|
||||
* by reversing left/right positions.
|
||||
* @param _isBrowser Whether the table is currently being rendered on the server or the client.
|
||||
*/
|
||||
constructor(private isNativeHtmlTable: boolean,
|
||||
private stickCellCss: string,
|
||||
public direction: Direction) { }
|
||||
public direction: Direction,
|
||||
private _isBrowser = true) { }
|
||||
|
||||
/**
|
||||
* Clears the sticky positioning styles from the row and its cells by resetting the `position`
|
||||
@@ -73,7 +75,7 @@ export class StickyStyler {
|
||||
rows: HTMLElement[], stickyStartStates: boolean[], stickyEndStates: boolean[]) {
|
||||
const hasStickyColumns =
|
||||
stickyStartStates.some(state => state) || stickyEndStates.some(state => state);
|
||||
if (!rows.length || !hasStickyColumns) {
|
||||
if (!rows.length || !hasStickyColumns || !this._isBrowser) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -111,6 +113,11 @@ export class StickyStyler {
|
||||
*
|
||||
*/
|
||||
stickRows(rowsToStick: HTMLElement[], stickyStates: boolean[], position: 'top' | 'bottom') {
|
||||
// Since we can't measure the rows on the server, we can't stick the rows properly.
|
||||
if (!this._isBrowser) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If positioning the rows to the bottom, reverse their order when evaluating the sticky
|
||||
// position such that the last row stuck will be "bottom: 0px" and so on.
|
||||
const rows = position === 'bottom' ? rowsToStick.reverse() : rowsToStick;
|
||||
|
||||
+21
-4
@@ -31,7 +31,9 @@ import {
|
||||
ViewChild,
|
||||
ViewContainerRef,
|
||||
ViewEncapsulation,
|
||||
Inject,
|
||||
} from '@angular/core';
|
||||
import {DOCUMENT} from '@angular/common';
|
||||
import {BehaviorSubject, Observable, of as observableOf, Subject, Subscription} from 'rxjs';
|
||||
import {takeUntil} from 'rxjs/operators';
|
||||
import {CdkColumnDef} from './cell';
|
||||
@@ -55,6 +57,7 @@ import {
|
||||
import {coerceBooleanProperty} from '@angular/cdk/coercion';
|
||||
import {StickyStyler} from './sticky-styler';
|
||||
import {Direction, Directionality} from '@angular/cdk/bidi';
|
||||
import {Platform} from '@angular/cdk/platform';
|
||||
|
||||
/** Interface used to provide an outlet for rows to be inserted into. */
|
||||
export interface RowOutlet {
|
||||
@@ -148,6 +151,8 @@ export interface RenderRow<T> {
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDestroy, OnInit {
|
||||
private _document: Document;
|
||||
|
||||
/** Latest data provided by the data source. */
|
||||
protected _data: T[] | ReadonlyArray<T>;
|
||||
|
||||
@@ -359,11 +364,19 @@ export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDes
|
||||
protected readonly _changeDetectorRef: ChangeDetectorRef,
|
||||
protected readonly _elementRef: ElementRef,
|
||||
@Attribute('role') role: string,
|
||||
@Optional() protected readonly _dir: Directionality) {
|
||||
@Optional() protected readonly _dir: Directionality,
|
||||
/**
|
||||
* @deprecated
|
||||
* @breaking-change 8.0.0 `_document` and `_platform` to
|
||||
* be made into a required parameters.
|
||||
*/
|
||||
@Inject(DOCUMENT) _document?: any,
|
||||
private _platform?: Platform) {
|
||||
if (!role) {
|
||||
this._elementRef.nativeElement.setAttribute('role', 'grid');
|
||||
}
|
||||
|
||||
this._document = _document;
|
||||
this._isNativeHtmlTable = this._elementRef.nativeElement.nodeName === 'TABLE';
|
||||
}
|
||||
|
||||
@@ -949,7 +962,9 @@ export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDes
|
||||
];
|
||||
|
||||
for (const section of sections) {
|
||||
const element = document.createElement(section.tag);
|
||||
// @breaking-change 8.0.0 remove the `|| document` once the `_document` is a required param.
|
||||
const documentRef = this._document || document;
|
||||
const element = documentRef.createElement(section.tag);
|
||||
element.appendChild(section.outlet.elementRef.nativeElement);
|
||||
this._elementRef.nativeElement.appendChild(element);
|
||||
}
|
||||
@@ -1001,7 +1016,9 @@ export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDes
|
||||
*/
|
||||
private _setupStickyStyler() {
|
||||
const direction: Direction = this._dir ? this._dir.value : 'ltr';
|
||||
this._stickyStyler = new StickyStyler(this._isNativeHtmlTable, this.stickyCssClass, direction);
|
||||
this._stickyStyler = new StickyStyler(this._isNativeHtmlTable,
|
||||
// @breaking-change 8.0.0 remove the null check for `this._platform`.
|
||||
this.stickyCssClass, direction, this._platform ? this._platform.isBrowser : true);
|
||||
(this._dir ? this._dir.change : observableOf<Direction>())
|
||||
.pipe(takeUntil(this._onDestroy))
|
||||
.subscribe(value => {
|
||||
@@ -1012,6 +1029,6 @@ export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDes
|
||||
}
|
||||
|
||||
/** Utility function that gets a merged list of the entries in a QueryList and values of a Set. */
|
||||
function mergeQueryListAndSet<T>(queryList: QueryList<T>, set: Set<T>): T[] {
|
||||
function mergeQueryListAndSet<T>(queryList: QueryList<T>, set: Set<T>): T[] {
|
||||
return queryList.toArray().concat(Array.from(set));
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ ng_module(
|
||||
"//src/lib/paginator",
|
||||
"//src/lib/sort",
|
||||
"//src/cdk/table",
|
||||
"//src/cdk/platform",
|
||||
"@rxjs",
|
||||
] + glob(["**/*.html"]),
|
||||
tsconfig = "//src/lib:tsconfig-build.json",
|
||||
|
||||
@@ -303,6 +303,21 @@
|
||||
<mat-row *cdkRowDef="let row; columns: tableColumns;"></mat-row>
|
||||
</mat-table>
|
||||
|
||||
<h2>Native table with sticky header and footer</h2>
|
||||
|
||||
<table mat-table [dataSource]="tableDataSource">
|
||||
<ng-container matColumnDef="userId">
|
||||
<th mat-header-cell *matHeaderCellDef>ID</th>
|
||||
<td mat-cell *matCellDef="let row">{{row.userId}}</td>
|
||||
<td mat-footer-cell *matFooterCellDef>ID</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="tableColumns; sticky: true"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: tableColumns;"></tr>
|
||||
<tr mat-footer-row *matFooterRowDef="tableColumns; sticky: true"></tr>
|
||||
</table>
|
||||
|
||||
|
||||
<h2>Selection list</h2>
|
||||
<mat-selection-list>
|
||||
<h3 mat-subheader>Groceries</h3>
|
||||
|
||||
Reference in New Issue
Block a user