diff --git a/packages/core/src/render3/view_manipulation.ts b/packages/core/src/render3/view_manipulation.ts index d5684e73387a..b4b206bc60ac 100644 --- a/packages/core/src/render3/view_manipulation.ts +++ b/packages/core/src/render3/view_manipulation.ts @@ -70,7 +70,8 @@ export function getLViewFromLContainer(lContainer: LContainer, index: number) */ export function shouldAddViewToDom( tNode: TNode, dehydratedView?: DehydratedContainerView|null): boolean { - return !dehydratedView || hasInSkipHydrationBlockFlag(tNode); + return !dehydratedView || !dehydratedView.firstChild?.parentElement || + hasInSkipHydrationBlockFlag(tNode); } export function addLViewToLContainer( diff --git a/packages/platform-server/test/hydration_spec.ts b/packages/platform-server/test/hydration_spec.ts index 448ff232811e..7d5dd23bed34 100644 --- a/packages/platform-server/test/hydration_spec.ts +++ b/packages/platform-server/test/hydration_spec.ts @@ -6284,6 +6284,52 @@ describe('platform-server hydration integration', () => { [4, 5].map(id => compRef.location.nativeElement.querySelector(`[id=${id}]`)); verifyAllNodesClaimedForHydration(clientRootNode, Array.from(clientRenderedItems)); }); + + it('should handle a reconciliation with swap', async () => { + @Component({ + selector: 'app', + standalone: true, + template: ` + @for(item of array2; track item) { +
{{ item }}
+ } + `, + }) + class SimpleComponent { + array2 = ['foo', 'bar', 'baz']; + + swap() { + this.array2.sort(); + } + } + + const html = await ssr(SimpleComponent); + const ssrContents = getAppContents(html); + + expect(ssrContents).toContain(`(appRef); + appRef.tick(); + + await whenStable(appRef); + + const root: HTMLElement = compRef.location.nativeElement; + const divs = root.querySelectorAll(`div`); + expect(divs.length).toBe(3); + + compRef.instance.swap(); + compRef.changeDetectorRef.detectChanges(); + + const divsAfterSwap = root.querySelectorAll(`div`); + expect(divsAfterSwap.length).toBe(3); + }); }); describe('Router', () => {