Skip to content

Commit fd77164

Browse files
thePunderWomanalxhub
authored andcommitted
fix(core): Prevents trying to trigger incremental hydration on CSR (#58366)
hydrate triggers were firing in CSR cases and attempting to find parent defer blocks. This prevents that from happening. In these cases, the defer block id will be empty. fixes: #58359 PR Close #58366
1 parent 98f54cb commit fd77164

File tree

3 files changed

+62
-9
lines changed

3 files changed

+62
-9
lines changed

packages/core/src/event_delegation_utils.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,17 @@ export const sharedMapFunction = (rEl: RElement, jsActionMap: Map<string, Set<El
7878
};
7979

8080
export function removeListenersFromBlocks(blockNames: string[], injector: Injector) {
81-
let blockList: Element[] = [];
82-
const jsActionMap = injector.get(BLOCK_ELEMENT_MAP);
83-
for (let blockName of blockNames) {
84-
if (jsActionMap.has(blockName)) {
85-
blockList = [...blockList, ...jsActionMap.get(blockName)!];
81+
if (blockNames.length > 0) {
82+
let blockList: Element[] = [];
83+
const jsActionMap = injector.get(BLOCK_ELEMENT_MAP);
84+
for (let blockName of blockNames) {
85+
if (jsActionMap.has(blockName)) {
86+
blockList = [...blockList, ...jsActionMap.get(blockName)!];
87+
}
8688
}
89+
const replayList = new Set(blockList);
90+
replayList.forEach(removeListeners);
8791
}
88-
const replayList = new Set(blockList);
89-
replayList.forEach(removeListeners);
9092
}
9193

9294
export const removeListeners = (el: Element) => {

packages/core/src/hydration/blocks.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ export async function hydrateFromBlockName(
6262
deferBlock: DeferBlock | null;
6363
hydratedBlocks: Set<string>;
6464
}> {
65+
if (blockName == null) {
66+
return {deferBlock: null, hydratedBlocks};
67+
}
6568
const deferBlockRegistry = injector.get(DeferBlockRegistry);
6669

6770
// Make sure we don't hydrate/trigger the same thing multiple times
@@ -116,4 +119,5 @@ export async function incrementallyHydrateFromBlockName(
116119
// the hydration process has finished, which could result in problems
117120
await whenStable(injector.get(ApplicationRef));
118121
}
122+
return Promise.resolve();
119123
}

packages/platform-server/test/incremental_hydration_spec.ts

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,17 @@ import {
1818
ɵwhenStable as whenStable,
1919
} from '@angular/core';
2020

21-
import {getAppContents, prepareEnvironmentAndHydrate, resetTViewsFor} from './dom_utils';
21+
import {getAppContents, hydrate, prepareEnvironmentAndHydrate, resetTViewsFor} from './dom_utils';
2222
import {getComponentRef, ssr, timeout} from './hydration_utils';
2323
import {getDocument} from '@angular/core/src/render3/interfaces/document';
2424
import {isPlatformServer} from '@angular/common';
25-
import {withEventReplay, withIncrementalHydration} from '@angular/platform-browser';
25+
import {
26+
provideClientHydration,
27+
withEventReplay,
28+
withIncrementalHydration,
29+
} from '@angular/platform-browser';
30+
import {TestBed} from '@angular/core/testing';
31+
import {PLATFORM_BROWSER_ID} from '@angular/common/src/platform_id';
2632

2733
describe('platform-server partial hydration integration', () => {
2834
const originalWindow = globalThis.window;
@@ -1393,6 +1399,47 @@ describe('platform-server partial hydration integration', () => {
13931399
}, 100_000);
13941400
});
13951401

1402+
describe('client side navigation', () => {
1403+
beforeEach(() => {
1404+
TestBed.configureTestingModule({
1405+
providers: [
1406+
{provide: PLATFORM_ID, useValue: PLATFORM_BROWSER_ID},
1407+
provideClientHydration(withIncrementalHydration()),
1408+
],
1409+
});
1410+
});
1411+
1412+
it('should not try to hydrate in CSR only cases', async () => {
1413+
@Component({
1414+
standalone: true,
1415+
selector: 'app',
1416+
template: `
1417+
<main (click)="fnA()">
1418+
@defer (hydrate when true) {
1419+
<article>
1420+
defer block rendered!
1421+
<span id="test" (click)="fnB()">{{value()}}</span>
1422+
</article>
1423+
} @placeholder {
1424+
<span>Outer block placeholder</span>
1425+
}
1426+
</main>
1427+
`,
1428+
})
1429+
class SimpleComponent {
1430+
value = signal('start');
1431+
fnA() {}
1432+
fnB() {
1433+
this.value.set('end');
1434+
}
1435+
}
1436+
const fixture = TestBed.createComponent(SimpleComponent);
1437+
fixture.detectChanges();
1438+
1439+
expect(fixture.nativeElement.innerHTML).toContain('Outer block placeholder');
1440+
});
1441+
});
1442+
13961443
describe('cleanup', () => {
13971444
it('should cleanup partial hydration blocks appropriately', async () => {
13981445
@Component({

0 commit comments

Comments
 (0)