Skip to content

Commit a7f87a8

Browse files
authored
fix(material/tabs): enable hydration (#28366)
Adds a workaround that allows us to enable hydration in the tab group component.
1 parent 4bae885 commit a7f87a8

File tree

5 files changed

+51
-7
lines changed

5 files changed

+51
-7
lines changed

src/material/tabs/tab-group.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@
5151
}
5252
</mat-tab-header>
5353

54+
<!--
55+
We need to project the content somewhere to avoid hydration errors. Some observations:
56+
1. This is only necessary on the server.
57+
2. We get a hydration error if there aren't any nodes after the `ng-content`.
58+
3. We get a hydration error if `ng-content` is wrapped in another element.
59+
-->
60+
@if (_isServer) {
61+
<ng-content/>
62+
}
63+
5464
<div
5565
class="mat-mdc-tab-body-wrapper"
5666
[class._mat-animation-noopable]="_animationMode === 'NoopAnimations'"

src/material/tabs/tab-group.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
ViewChild,
2525
ViewEncapsulation,
2626
booleanAttribute,
27+
inject,
2728
numberAttribute,
2829
} from '@angular/core';
2930
import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';
@@ -38,6 +39,7 @@ import {MatTabBody} from './tab-body';
3839
import {CdkPortalOutlet} from '@angular/cdk/portal';
3940
import {NgClass} from '@angular/common';
4041
import {MatTabLabelWrapper} from './tab-label-wrapper';
42+
import {Platform} from '@angular/cdk/platform';
4143

4244
/** Used to generate unique ID's for each tab component */
4345
let nextId = 0;
@@ -75,7 +77,6 @@ const ENABLE_BACKGROUND_INPUT = true;
7577
},
7678
],
7779
host: {
78-
'ngSkipHydration': '',
7980
'class': 'mat-mdc-tab-group',
8081
'[class]': '"mat-" + (color || "primary")',
8182
'[class.mat-mdc-tab-group-dynamic-height]': 'dynamicHeight',
@@ -248,6 +249,9 @@ export class MatTabGroup implements AfterContentInit, AfterContentChecked, OnDes
248249

249250
private _groupId: number;
250251

252+
/** Whether the tab group is rendered on the server. */
253+
protected _isServer: boolean = !inject(Platform).isBrowser;
254+
251255
constructor(
252256
readonly _elementRef: ElementRef,
253257
private _changeDetectorRef: ChangeDetectorRef,

src/material/tabs/tab.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ export const MAT_TAB_GROUP = new InjectionToken<any>('MAT_TAB_GROUP');
4747
exportAs: 'matTab',
4848
providers: [{provide: MAT_TAB, useExisting: MatTab}],
4949
standalone: true,
50+
host: {
51+
// This element will be rendered on the server in order to support hydration.
52+
// Hide it so it doesn't cause a layout shift when it's removed on the client.
53+
'hidden': '',
54+
},
5055
})
5156
export class MatTab implements OnInit, OnChanges, OnDestroy {
5257
/** whether the tab is disabled. */

src/universal-app/kitchen-sink/kitchen-sink.html

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -307,13 +307,37 @@ <h2>Tabs</h2>
307307
-->
308308
<mat-tab-group [selectedIndex]="1">
309309
<mat-tab label="Overview">
310-
The overview
310+
<h3>The overview</h3>
311+
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Doloremque assumenda doloribus,
312+
rerum temporibus fugit aliquid adipisci aliquam eaque sint voluptas dolore cumque voluptatibus
313+
quam quod. Quasi adipisci officia similique in?</p>
314+
<p>Deleniti neque placeat magnam, voluptatibus eligendi illo consectetur dolore minima dolorem
315+
nemo suscipit dolorum accusantium? Numquam officia culpa itaque qui repudiandae nulla,
316+
laboriosam nihil molestiae ad aut perferendis alias amet.</p>
317+
<p>Officia esse temporibus consequatur ipsa! Veritatis alias facere amet reiciendis sint
318+
impedit atque iste doloremque dolor? Ullam, aspernatur? Alias, fuga! At dolorum odio
319+
molestiae laudantium nihil alias inventore veritatis voluptatum.</p>
320+
<button mat-raised-button color="primary">See the overview</button>
311321
</mat-tab>
312322
<mat-tab>
313-
<ng-template mat-tab-label>
314-
API docs
315-
</ng-template>
316-
The API docs
323+
<ng-template mat-tab-label>API docs</ng-template>
324+
<h3>The API docs</h3>
325+
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolorum facere quasi natus rerum
326+
quae, nisi, quis, voluptate assumenda necessitatibus labore illo. Illum ipsum consequatur,
327+
excepturi aspernatur odio veritatis sint perferendis!</p>
328+
<p>Dicta ex laborum repudiandae nesciunt. Ea asperiores quo totam velit! Aliquid cum laudantium
329+
officiis molestias, excepturi odio, autem magni dignissimos perspiciatis, amet qui! Dolorem
330+
molestiae similique necessitatibus cupiditate ipsa aspernatur?</p>
331+
<button mat-raised-button color="accent">See the API docs</button>
332+
</mat-tab>
333+
334+
<mat-tab>
335+
<ng-template mat-tab-label>Examples</ng-template>
336+
<h3>The examples</h3>
337+
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Modi animi saepe, optio sequi
338+
accusantium, eos perspiciatis reprehenderit, nobis exercitationem sunt ducimus molestiae
339+
laborum inventore itaque incidunt. Neque dolorum adipisci quidem.</p>
340+
<button mat-raised-button color="warn">See the examples</button>
317341
</mat-tab>
318342
</mat-tab-group>
319343

tools/public_api_guard/material/tabs.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ export class MatTabGroup implements AfterContentInit, AfterContentChecked, OnDes
281281
_getTabLabelId(i: number): string;
282282
_handleClick(tab: MatTab, tabHeader: MatTabGroupBaseHeader, index: number): void;
283283
headerPosition: MatTabHeaderPosition;
284+
protected _isServer: boolean;
284285
// (undocumented)
285286
static ngAcceptInputType_contentTabIndex: unknown;
286287
// (undocumented)
@@ -319,7 +320,7 @@ export class MatTabGroup implements AfterContentInit, AfterContentChecked, OnDes
319320
_tabs: QueryList<MatTab>;
320321
updatePagination(): void;
321322
// (undocumented)
322-
static ɵcmp: i0.ɵɵComponentDeclaration<MatTabGroup, "mat-tab-group", ["matTabGroup"], { "color": { "alias": "color"; "required": false; }; "fitInkBarToContent": { "alias": "fitInkBarToContent"; "required": false; }; "stretchTabs": { "alias": "mat-stretch-tabs"; "required": false; }; "dynamicHeight": { "alias": "dynamicHeight"; "required": false; }; "selectedIndex": { "alias": "selectedIndex"; "required": false; }; "headerPosition": { "alias": "headerPosition"; "required": false; }; "animationDuration": { "alias": "animationDuration"; "required": false; }; "contentTabIndex": { "alias": "contentTabIndex"; "required": false; }; "disablePagination": { "alias": "disablePagination"; "required": false; }; "disableRipple": { "alias": "disableRipple"; "required": false; }; "preserveContent": { "alias": "preserveContent"; "required": false; }; "backgroundColor": { "alias": "backgroundColor"; "required": false; }; }, { "selectedIndexChange": "selectedIndexChange"; "focusChange": "focusChange"; "animationDone": "animationDone"; "selectedTabChange": "selectedTabChange"; }, ["_allTabs"], never, true, never>;
323+
static ɵcmp: i0.ɵɵComponentDeclaration<MatTabGroup, "mat-tab-group", ["matTabGroup"], { "color": { "alias": "color"; "required": false; }; "fitInkBarToContent": { "alias": "fitInkBarToContent"; "required": false; }; "stretchTabs": { "alias": "mat-stretch-tabs"; "required": false; }; "dynamicHeight": { "alias": "dynamicHeight"; "required": false; }; "selectedIndex": { "alias": "selectedIndex"; "required": false; }; "headerPosition": { "alias": "headerPosition"; "required": false; }; "animationDuration": { "alias": "animationDuration"; "required": false; }; "contentTabIndex": { "alias": "contentTabIndex"; "required": false; }; "disablePagination": { "alias": "disablePagination"; "required": false; }; "disableRipple": { "alias": "disableRipple"; "required": false; }; "preserveContent": { "alias": "preserveContent"; "required": false; }; "backgroundColor": { "alias": "backgroundColor"; "required": false; }; }, { "selectedIndexChange": "selectedIndexChange"; "focusChange": "focusChange"; "animationDone": "animationDone"; "selectedTabChange": "selectedTabChange"; }, ["_allTabs"], ["*"], true, never>;
323324
// (undocumented)
324325
static ɵfac: i0.ɵɵFactoryDeclaration<MatTabGroup, [null, null, { optional: true; }, { optional: true; }]>;
325326
}

0 commit comments

Comments
 (0)