mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
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
This commit is contained in:
committed by
Pawel Kozlowski
parent
031e40e6dd
commit
ae83646704
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user