From d4449fce21bebbb18f9e1341f1675cdbec7e83ac Mon Sep 17 00:00:00 2001 From: Andrew Kushnir Date: Tue, 13 Aug 2024 18:07:52 -0700 Subject: [PATCH] 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 (``), but such nodes do not have DOM representation. Resolves #56750. PR Close #57383 --- packages/core/src/hydration/annotate.ts | 6 ++ .../platform-server/test/hydration_spec.ts | 66 +++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/packages/core/src/hydration/annotate.ts b/packages/core/src/hydration/annotate.ts index 75258ca04d2..1f9566effe4 100644 --- a/packages/core/src/hydration/annotate.ts +++ b/packages/core/src/hydration/annotate.ts @@ -583,6 +583,12 @@ function conditionallyAnnotateNodePath( lView: LView, excludedParentNodes: Set | null, ) { + if (isProjectionTNode(tNode)) { + // Do not annotate projection nodes (), since + // they don't have a corresponding DOM node representing them. + return; + } + // Handle case #1 described above. if ( tNode.projectionNext && diff --git a/packages/platform-server/test/hydration_spec.ts b/packages/platform-server/test/hydration_spec.ts index 872aacf3e4e..ac25d07ff43 100644 --- a/packages/platform-server/test/hydration_spec.ts +++ b/packages/platform-server/test/hydration_spec.ts @@ -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: ``, + }) + class DropdownContentComponent {} + + @Component({ + standalone: true, + selector: 'app-dropdown', + template: ` + @if (false) { + + } + `, + }) + class DropdownComponent {} + + @Component({ + standalone: true, + imports: [DropdownComponent, DropdownContentComponent], + selector: 'app-menu', + template: ` + + + + + + `, + }) + class MenuComponent {} + + @Component({ + selector: 'app', + standalone: true, + imports: [MenuComponent], + template: ` + + Menu Content + + `, + }) + class SimpleComponent {} + + const html = await ssr(SimpleComponent); + const ssrContents = getAppContents(html); + + expect(ssrContents).toContain('(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,