Skip to content

Commit 76709d5

Browse files
JeanMechepkozlowski-opensource
authored andcommitted
fix(core): Handle @let declaration with array when preparingForHydration (#57816)
Before this commit, `@let` decleration with an array where mistaken for a component in the lView and throwing an unexpected error. This commit fixes this. PR Close #57816
1 parent dbd697c commit 76709d5

File tree

3 files changed

+40
-3
lines changed

3 files changed

+40
-3
lines changed

packages/core/src/hydration/annotate.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {assertTNode} from '../render3/assert';
1414
import {collectNativeNodes, collectNativeNodesInLContainer} from '../render3/collect_native_nodes';
1515
import {getComponentDef} from '../render3/definition';
1616
import {CONTAINER_HEADER_OFFSET, LContainer} from '../render3/interfaces/container';
17-
import {isTNodeShape, TNode, TNodeType} from '../render3/interfaces/node';
17+
import {isLetDeclaration, isTNodeShape, TNode, TNodeType} from '../render3/interfaces/node';
1818
import {RElement} from '../render3/interfaces/renderer_dom';
1919
import {
2020
hasI18n,
@@ -400,7 +400,7 @@ function serializeLView(lView: LView, context: HydrationContext): SerializedView
400400
: null;
401401
// Iterate over DOM element references in an LView.
402402
for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
403-
const tNode = tView.data[i] as TNode;
403+
const tNode = tView.data[i];
404404
const noOffsetIndex = i - HEADER_OFFSET;
405405

406406
// Attempt to serialize any i18n data for the given slot. We do this first, as i18n
@@ -526,8 +526,10 @@ function serializeLView(lView: LView, context: HydrationContext): SerializedView
526526

527527
ngh[CONTAINERS] ??= {};
528528
ngh[CONTAINERS][noOffsetIndex] = serializeLContainer(lView[i], context);
529-
} else if (Array.isArray(lView[i])) {
529+
} else if (Array.isArray(lView[i]) && !isLetDeclaration(tNode)) {
530530
// This is a component, annotate the host node with an `ngh` attribute.
531+
// Note: Let declarations that return an array are also storing an array in the LView,
532+
// we need to exclude them.
531533
const targetNode = unwrapRNode(lView[i][HOST]!);
532534
if (!(targetNode as HTMLElement).hasAttribute(SKIP_HYDRATION_ATTR_NAME)) {
533535
annotateHostElementForHydration(targetNode as RElement, lView[i], context);

packages/core/src/render3/interfaces/node.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ export function isTNodeShape(value: unknown): value is TNode {
122122
);
123123
}
124124

125+
export function isLetDeclaration(tNode: TNode): boolean {
126+
return !!(tNode.type & TNodeType.LetDeclaration);
127+
}
128+
125129
/**
126130
* Corresponds to the TNode.flags property.
127131
*/

packages/platform-server/test/hydration_spec.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7917,6 +7917,37 @@ describe('platform-server hydration integration', () => {
79177917
verifyClientAndSSRContentsMatch(ssrContents, clientRootNode);
79187918
expect(clientRootNode.textContent).toContain('inside before|after');
79197919
});
7920+
7921+
it('should handle let declaration with array inside of an embedded view', async () => {
7922+
@Component({
7923+
standalone: true,
7924+
selector: 'app',
7925+
template: `
7926+
@let foo = ['foo'];
7927+
@if (true) {
7928+
{{foo}}
7929+
}
7930+
`,
7931+
})
7932+
class SimpleComponent {}
7933+
7934+
const html = await ssr(SimpleComponent);
7935+
const ssrContents = getAppContents(html);
7936+
7937+
expect(ssrContents).toContain('<app ngh');
7938+
expect(ssrContents).toContain('foo');
7939+
7940+
resetTViewsFor(SimpleComponent);
7941+
7942+
const appRef = await renderAndHydrate(doc, html, SimpleComponent);
7943+
const compRef = getComponentRef<SimpleComponent>(appRef);
7944+
appRef.tick();
7945+
7946+
const clientRootNode = compRef.location.nativeElement;
7947+
verifyAllNodesClaimedForHydration(clientRootNode);
7948+
verifyClientAndSSRContentsMatch(ssrContents, clientRootNode);
7949+
expect(clientRootNode.textContent).toContain('foo');
7950+
});
79207951
});
79217952

79227953
describe('Router', () => {

0 commit comments

Comments
 (0)