Skip to content

Commit

Permalink
fix(core): handle elements with local refs in event replay serializat…
Browse files Browse the repository at this point in the history
…ion logic

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 angular#56073.
  • Loading branch information
AndrewKushnir committed May 24, 2024
1 parent 8250238 commit 2ca7773
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 3 deletions.
10 changes: 7 additions & 3 deletions packages/core/src/hydration/annotate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,9 +393,6 @@ function serializeLView(lView: LView, context: HydrationContext): SerializedView
for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
const tNode = tView.data[i] as TNode;
const noOffsetIndex = i - HEADER_OFFSET;
if (nativeElementsToEventTypes) {
setJSActionAttribute(tNode, lView[i], nativeElementsToEventTypes);
}

// Attempt to serialize any i18n data for the given slot. We do this first, as i18n
// has its own process for serialization.
Expand Down Expand Up @@ -434,6 +431,13 @@ function serializeLView(lView: LView, context: HydrationContext): SerializedView
appendDisconnectedNodeIndex(ngh, tNode);
continue;
}

if (nativeElementsToEventTypes) {
// Attach `jsaction` attribute to elements that have registered listeners,
// thus potentially having a need to do an event replay.
setJSActionAttribute(tNode, lView[i], nativeElementsToEventTypes);
}

if (Array.isArray(tNode.projection)) {
for (const projectionHeadTNode of tNode.projection) {
// We may have `null`s in slots with no projected content.
Expand Down
26 changes: 26 additions & 0 deletions packages/platform-server/test/event_replay_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,32 @@ describe('event replay', () => {
}
}

it('should work for elements with local refs', async () => {
const onClickSpy = jasmine.createSpy();

@Component({
selector: 'app',
standalone: true,
template: `
<button id="btn" (click)="onClick()" #localRef></button>
`,
})
class AppComponent {
onClick = onClickSpy;
}
const html = await ssr(AppComponent);
const ssrContents = getAppContents(html);
render(doc, ssrContents);
resetTViewsFor(AppComponent);
const btn = doc.getElementById('btn')!;
btn.click();
const appRef = await hydrate(doc, AppComponent, {
hydrationFeatures: [withEventReplay()],
});
appRef.tick();
expect(onClickSpy).toHaveBeenCalled();
});

it('should route to the appropriate component with content projection', async () => {
const outerOnClickSpy = jasmine.createSpy();
const innerOnClickSpy = jasmine.createSpy();
Expand Down

0 comments on commit 2ca7773

Please sign in to comment.