Skip to content

Commit ae83646

Browse files
fix(core): handle elements with local refs in event replay serialization logic (#56076)
Previously, the event replay serialization logic was located before we verify that a `TNode` exists. `TNode`s may not exist in `tView.data` array in several cases, including cases when there is a local ref used on an element: in this case an extra slot in `LView` contains a reference to the same element and `TNode` is not needed. This commit moves the event replay serialization logic a bit lower, after we check for TNode presence. Resolves #56073. PR Close #56076
1 parent 031e40e commit ae83646

File tree

2 files changed

+33
-3
lines changed

2 files changed

+33
-3
lines changed

packages/core/src/hydration/annotate.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -393,9 +393,6 @@ function serializeLView(lView: LView, context: HydrationContext): SerializedView
393393
for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
394394
const tNode = tView.data[i] as TNode;
395395
const noOffsetIndex = i - HEADER_OFFSET;
396-
if (nativeElementsToEventTypes) {
397-
setJSActionAttribute(tNode, lView[i], nativeElementsToEventTypes);
398-
}
399396

400397
// Attempt to serialize any i18n data for the given slot. We do this first, as i18n
401398
// has its own process for serialization.
@@ -434,6 +431,13 @@ function serializeLView(lView: LView, context: HydrationContext): SerializedView
434431
appendDisconnectedNodeIndex(ngh, tNode);
435432
continue;
436433
}
434+
435+
if (nativeElementsToEventTypes) {
436+
// Attach `jsaction` attribute to elements that have registered listeners,
437+
// thus potentially having a need to do an event replay.
438+
setJSActionAttribute(tNode, lView[i], nativeElementsToEventTypes);
439+
}
440+
437441
if (Array.isArray(tNode.projection)) {
438442
for (const projectionHeadTNode of tNode.projection) {
439443
// We may have `null`s in slots with no projected content.

packages/platform-server/test/event_replay_spec.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,32 @@ describe('event replay', () => {
103103
}
104104
}
105105

106+
it('should work for elements with local refs', async () => {
107+
const onClickSpy = jasmine.createSpy();
108+
109+
@Component({
110+
selector: 'app',
111+
standalone: true,
112+
template: `
113+
<button id="btn" (click)="onClick()" #localRef></button>
114+
`,
115+
})
116+
class AppComponent {
117+
onClick = onClickSpy;
118+
}
119+
const html = await ssr(AppComponent);
120+
const ssrContents = getAppContents(html);
121+
render(doc, ssrContents);
122+
resetTViewsFor(AppComponent);
123+
const btn = doc.getElementById('btn')!;
124+
btn.click();
125+
const appRef = await hydrate(doc, AppComponent, {
126+
hydrationFeatures: [withEventReplay()],
127+
});
128+
appRef.tick();
129+
expect(onClickSpy).toHaveBeenCalled();
130+
});
131+
106132
it('should route to the appropriate component with content projection', async () => {
107133
const outerOnClickSpy = jasmine.createSpy();
108134
const innerOnClickSpy = jasmine.createSpy();

0 commit comments

Comments
 (0)