mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
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
This commit is contained in:
parent
98f54cb134
commit
fd7716440b
3 changed files with 62 additions and 9 deletions
|
|
@ -78,15 +78,17 @@ export const sharedMapFunction = (rEl: RElement, jsActionMap: Map<string, Set<El
|
|||
};
|
||||
|
||||
export function removeListenersFromBlocks(blockNames: string[], injector: Injector) {
|
||||
let blockList: Element[] = [];
|
||||
const jsActionMap = injector.get(BLOCK_ELEMENT_MAP);
|
||||
for (let blockName of blockNames) {
|
||||
if (jsActionMap.has(blockName)) {
|
||||
blockList = [...blockList, ...jsActionMap.get(blockName)!];
|
||||
if (blockNames.length > 0) {
|
||||
let blockList: Element[] = [];
|
||||
const jsActionMap = injector.get(BLOCK_ELEMENT_MAP);
|
||||
for (let blockName of blockNames) {
|
||||
if (jsActionMap.has(blockName)) {
|
||||
blockList = [...blockList, ...jsActionMap.get(blockName)!];
|
||||
}
|
||||
}
|
||||
const replayList = new Set(blockList);
|
||||
replayList.forEach(removeListeners);
|
||||
}
|
||||
const replayList = new Set(blockList);
|
||||
replayList.forEach(removeListeners);
|
||||
}
|
||||
|
||||
export const removeListeners = (el: Element) => {
|
||||
|
|
|
|||
|
|
@ -62,6 +62,9 @@ export async function hydrateFromBlockName(
|
|||
deferBlock: DeferBlock | null;
|
||||
hydratedBlocks: Set<string>;
|
||||
}> {
|
||||
if (blockName == null) {
|
||||
return {deferBlock: null, hydratedBlocks};
|
||||
}
|
||||
const deferBlockRegistry = injector.get(DeferBlockRegistry);
|
||||
|
||||
// Make sure we don't hydrate/trigger the same thing multiple times
|
||||
|
|
@ -116,4 +119,5 @@ export async function incrementallyHydrateFromBlockName(
|
|||
// the hydration process has finished, which could result in problems
|
||||
await whenStable(injector.get(ApplicationRef));
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,11 +18,17 @@ import {
|
|||
ɵwhenStable as whenStable,
|
||||
} from '@angular/core';
|
||||
|
||||
import {getAppContents, prepareEnvironmentAndHydrate, resetTViewsFor} from './dom_utils';
|
||||
import {getAppContents, hydrate, prepareEnvironmentAndHydrate, resetTViewsFor} from './dom_utils';
|
||||
import {getComponentRef, ssr, timeout} from './hydration_utils';
|
||||
import {getDocument} from '@angular/core/src/render3/interfaces/document';
|
||||
import {isPlatformServer} from '@angular/common';
|
||||
import {withEventReplay, withIncrementalHydration} from '@angular/platform-browser';
|
||||
import {
|
||||
provideClientHydration,
|
||||
withEventReplay,
|
||||
withIncrementalHydration,
|
||||
} from '@angular/platform-browser';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {PLATFORM_BROWSER_ID} from '@angular/common/src/platform_id';
|
||||
|
||||
describe('platform-server partial hydration integration', () => {
|
||||
const originalWindow = globalThis.window;
|
||||
|
|
@ -1393,6 +1399,47 @@ describe('platform-server partial hydration integration', () => {
|
|||
}, 100_000);
|
||||
});
|
||||
|
||||
describe('client side navigation', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{provide: PLATFORM_ID, useValue: PLATFORM_BROWSER_ID},
|
||||
provideClientHydration(withIncrementalHydration()),
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should not try to hydrate in CSR only cases', async () => {
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'app',
|
||||
template: `
|
||||
<main (click)="fnA()">
|
||||
@defer (hydrate when true) {
|
||||
<article>
|
||||
defer block rendered!
|
||||
<span id="test" (click)="fnB()">{{value()}}</span>
|
||||
</article>
|
||||
} @placeholder {
|
||||
<span>Outer block placeholder</span>
|
||||
}
|
||||
</main>
|
||||
`,
|
||||
})
|
||||
class SimpleComponent {
|
||||
value = signal('start');
|
||||
fnA() {}
|
||||
fnB() {
|
||||
this.value.set('end');
|
||||
}
|
||||
}
|
||||
const fixture = TestBed.createComponent(SimpleComponent);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.nativeElement.innerHTML).toContain('Outer block placeholder');
|
||||
});
|
||||
});
|
||||
|
||||
describe('cleanup', () => {
|
||||
it('should cleanup partial hydration blocks appropriately', async () => {
|
||||
@Component({
|
||||
|
|
|
|||
Loading…
Reference in a new issue