Skip to content

Commit add3cd4

Browse files
authored
fix(cdk/table): error if outlets are assigned too early (#28551)
Fixes that the table was throwing an error if all outlets are assigned before `ngAfterContentInit`. Fixes #28538.
1 parent f1e82da commit add3cd4

File tree

3 files changed

+36
-11
lines changed

3 files changed

+36
-11
lines changed

src/cdk/table/sticky-styler.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -263,10 +263,12 @@ export class StickyStyler {
263263
this._coalescedStyleScheduler.schedule(() => {
264264
const tfoot = tableElement.querySelector('tfoot')!;
265265

266-
if (stickyStates.some(state => !state)) {
267-
this._removeStickyStyle(tfoot, ['bottom']);
268-
} else {
269-
this._addStickyStyle(tfoot, 'bottom', 0, false);
266+
if (tfoot) {
267+
if (stickyStates.some(state => !state)) {
268+
this._removeStickyStyle(tfoot, ['bottom']);
269+
} else {
270+
this._addStickyStyle(tfoot, 'bottom', 0, false);
271+
}
270272
}
271273
});
272274
}

src/cdk/table/table.ts

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {ViewportRuler} from '@angular/cdk/scrolling';
2424
import {DOCUMENT} from '@angular/common';
2525
import {
2626
AfterContentChecked,
27+
AfterContentInit,
2728
Attribute,
2829
ChangeDetectionStrategy,
2930
ChangeDetectorRef,
@@ -285,7 +286,9 @@ export interface RenderRow<T> {
285286
standalone: true,
286287
imports: [HeaderRowOutlet, DataRowOutlet, NoDataRowOutlet, FooterRowOutlet],
287288
})
288-
export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDestroy, OnInit {
289+
export class CdkTable<T>
290+
implements AfterContentInit, AfterContentChecked, CollectionViewer, OnDestroy, OnInit
291+
{
289292
private _document: Document;
290293

291294
/** Latest data provided by the data source. */
@@ -433,7 +436,10 @@ export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDes
433436
private _isShowingNoDataRow = false;
434437

435438
/** Whether the table has rendered out all the outlets for the first time. */
436-
private _hasRendered = false;
439+
private _hasAllOutlets = false;
440+
441+
/** Whether the table is done initializing. */
442+
private _hasInitialized = false;
437443

438444
/** Aria role to apply to the table's cells based on the table's own role. */
439445
_getCellRole(): string | null {
@@ -641,9 +647,13 @@ export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDes
641647
});
642648
}
643649

650+
ngAfterContentInit() {
651+
this._hasInitialized = true;
652+
}
653+
644654
ngAfterContentChecked() {
645655
// Only start re-rendering in `ngAfterContentChecked` after the first render.
646-
if (this._hasRendered) {
656+
if (this._canRender()) {
647657
this._render();
648658
}
649659
}
@@ -902,17 +912,27 @@ export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDes
902912
// Also we can't use queries to resolve the outlets, because they're wrapped in a
903913
// conditional, so we have to rely on them being assigned via DI.
904914
if (
905-
!this._hasRendered &&
915+
!this._hasAllOutlets &&
906916
this._rowOutlet &&
907917
this._headerRowOutlet &&
908918
this._footerRowOutlet &&
909919
this._noDataRowOutlet
910920
) {
911-
this._hasRendered = true;
912-
this._render();
921+
this._hasAllOutlets = true;
922+
923+
// In some setups this may fire before `ngAfterContentInit`
924+
// so we need a check here. See #28538.
925+
if (this._canRender()) {
926+
this._render();
927+
}
913928
}
914929
}
915930

931+
/** Whether the table has all the information to start rendering. */
932+
private _canRender(): boolean {
933+
return this._hasAllOutlets && this._hasInitialized;
934+
}
935+
916936
/** Renders the table if its state has changed. */
917937
private _render(): void {
918938
// Cache the row and column definitions gathered by ContentChildren and programmatic injection.

tools/public_api_guard/cdk/table.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
```ts
66

77
import { AfterContentChecked } from '@angular/core';
8+
import { AfterContentInit } from '@angular/core';
89
import { BehaviorSubject } from 'rxjs';
910
import { ChangeDetectorRef } from '@angular/core';
1011
import { CollectionViewer } from '@angular/cdk/collections';
@@ -288,7 +289,7 @@ export class CdkRowDef<T> extends BaseRowDef {
288289
}
289290

290291
// @public
291-
export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDestroy, OnInit {
292+
export class CdkTable<T> implements AfterContentInit, AfterContentChecked, CollectionViewer, OnDestroy, OnInit {
292293
constructor(_differs: IterableDiffers, _changeDetectorRef: ChangeDetectorRef, _elementRef: ElementRef, role: string, _dir: Directionality, _document: any, _platform: Platform, _viewRepeater: _ViewRepeater<T, RenderRow<T>, RowContext<T>>, _coalescedStyleScheduler: _CoalescedStyleScheduler, _viewportRuler: ViewportRuler,
293294
_stickyPositioningListener: StickyPositioningListener,
294295
_ngZone?: NgZone | undefined);
@@ -337,6 +338,8 @@ export class CdkTable<T> implements AfterContentChecked, CollectionViewer, OnDes
337338
// (undocumented)
338339
ngAfterContentChecked(): void;
339340
// (undocumented)
341+
ngAfterContentInit(): void;
342+
// (undocumented)
340343
ngOnDestroy(): void;
341344
// (undocumented)
342345
ngOnInit(): void;

0 commit comments

Comments
 (0)