mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
fix(core): handle hydration of components that project content conditionally (#57383)
This commit fixes an issue when hydration serialization tries to calculate DOM path to a content projection node (`<ng-content>`), but such nodes do not have DOM representation. Resolves #56750. PR Close #57383
This commit is contained in:
parent
751880a16a
commit
d4449fce21
2 changed files with 72 additions and 0 deletions
|
|
@ -583,6 +583,12 @@ function conditionallyAnnotateNodePath(
|
|||
lView: LView<unknown>,
|
||||
excludedParentNodes: Set<number> | null,
|
||||
) {
|
||||
if (isProjectionTNode(tNode)) {
|
||||
// Do not annotate projection nodes (<ng-content />), since
|
||||
// they don't have a corresponding DOM node representing them.
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle case #1 described above.
|
||||
if (
|
||||
tNode.projectionNext &&
|
||||
|
|
|
|||
|
|
@ -5685,6 +5685,72 @@ describe('platform-server hydration integration', () => {
|
|||
verifyClientAndSSRContentsMatch(ssrContents, clientRootNode);
|
||||
});
|
||||
|
||||
it('should support cases when some element nodes are not projected', async () => {
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'app-dropdown-content',
|
||||
template: `<ng-content />`,
|
||||
})
|
||||
class DropdownContentComponent {}
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'app-dropdown',
|
||||
template: `
|
||||
@if (false) {
|
||||
<ng-content select="app-dropdown-content" />
|
||||
}
|
||||
`,
|
||||
})
|
||||
class DropdownComponent {}
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [DropdownComponent, DropdownContentComponent],
|
||||
selector: 'app-menu',
|
||||
template: `
|
||||
<app-dropdown>
|
||||
<app-dropdown-content>
|
||||
<ng-content />
|
||||
</app-dropdown-content>
|
||||
</app-dropdown>
|
||||
`,
|
||||
})
|
||||
class MenuComponent {}
|
||||
|
||||
@Component({
|
||||
selector: 'app',
|
||||
standalone: true,
|
||||
imports: [MenuComponent],
|
||||
template: `
|
||||
<app-menu>
|
||||
Menu Content
|
||||
</app-menu>
|
||||
`,
|
||||
})
|
||||
class SimpleComponent {}
|
||||
|
||||
const html = await ssr(SimpleComponent);
|
||||
const ssrContents = getAppContents(html);
|
||||
|
||||
expect(ssrContents).toContain('<app ngh');
|
||||
|
||||
resetTViewsFor(
|
||||
SimpleComponent,
|
||||
MenuComponent,
|
||||
DropdownComponent,
|
||||
DropdownContentComponent,
|
||||
);
|
||||
|
||||
const appRef = await renderAndHydrate(doc, html, SimpleComponent);
|
||||
const compRef = getComponentRef<SimpleComponent>(appRef);
|
||||
appRef.tick();
|
||||
|
||||
const clientRootNode = compRef.location.nativeElement;
|
||||
verifyAllNodesClaimedForHydration(clientRootNode);
|
||||
verifyClientAndSSRContentsMatch(ssrContents, clientRootNode);
|
||||
});
|
||||
|
||||
it('should support cases when view containers are not projected', async () => {
|
||||
@Component({
|
||||
standalone: true,
|
||||
|
|
|
|||
Loading…
Reference in a new issue