From 4d7fe48b4b54bf31ece95704f41abb041759d310 Mon Sep 17 00:00:00 2001 From: Matthieu Riegler Date: Tue, 28 Nov 2023 14:43:08 +0100 Subject: [PATCH] fix(core): Repeater swap after hydration should reinsert element After hydration, a swap remove an item but didn't reattached it if there was a dehydrated node. This commit fixes this. Fixes #53163 --- .../core/src/render3/view_manipulation.ts | 3 +- .../platform-server/test/hydration_spec.ts | 46 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) 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', () => {