@@ -28,12 +28,9 @@ import {
2828} from '@angular/platform-browser' ;
2929import { TestBed } from '@angular/core/testing' ;
3030import { PLATFORM_BROWSER_ID } from '@angular/common/src/platform_id' ;
31- import { DEHYDRATED_BLOCK_REGISTRY , DehydratedBlockRegistry } from '@angular/core/src/defer/registry' ;
31+ import { DEHYDRATED_BLOCK_REGISTRY } from '@angular/core/src/defer/registry' ;
3232import { JSACTION_BLOCK_ELEMENT_MAP } from '@angular/core/src/hydration/tokens' ;
33- import {
34- EventContractDetails ,
35- JSACTION_EVENT_CONTRACT ,
36- } from '@angular/core/src/event_delegation_utils' ;
33+ import { JSACTION_EVENT_CONTRACT } from '@angular/core/src/event_delegation_utils' ;
3734
3835describe ( 'platform-server partial hydration integration' , ( ) => {
3936 const originalWindow = globalThis . window ;
@@ -1445,6 +1442,88 @@ describe('platform-server partial hydration integration', () => {
14451442 } ) ;
14461443 } ) ;
14471444
1445+ describe ( 'control flow' , ( ) => {
1446+ it ( 'should support hydration for all items in a for loop' , async ( ) => {
1447+ @Component ( {
1448+ standalone : true ,
1449+ selector : 'app' ,
1450+ template : `
1451+ <main>
1452+ @defer (on interaction; hydrate on interaction) {
1453+ <div id="main" (click)="fnA()">
1454+ <p>Main defer block rendered!</p>
1455+ @for (item of items; track $index) {
1456+ @defer (on interaction; hydrate on interaction) {
1457+ <article id="item-{{item}}">
1458+ defer block {{item}} rendered!
1459+ <span (click)="fnB()">{{value()}}</span>
1460+ </article>
1461+ } @placeholder {
1462+ <span>Outer block placeholder</span>
1463+ }
1464+ }
1465+ </div>
1466+ } @placeholder {
1467+ <span>Outer block placeholder</span>
1468+ }
1469+ </main>
1470+ ` ,
1471+ } )
1472+ class SimpleComponent {
1473+ value = signal ( 'start' ) ;
1474+ items = [ 1 , 2 , 3 , 4 , 5 , 6 ] ;
1475+ fnA ( ) { }
1476+ fnB ( ) {
1477+ this . value . set ( 'end' ) ;
1478+ }
1479+ }
1480+
1481+ const appId = 'custom-app-id' ;
1482+ const providers = [ { provide : APP_ID , useValue : appId } ] ;
1483+ const hydrationFeatures = ( ) => [ withIncrementalHydration ( ) ] ;
1484+
1485+ const html = await ssr ( SimpleComponent , { envProviders : providers , hydrationFeatures} ) ;
1486+ const ssrContents = getAppContents ( html ) ;
1487+
1488+ // <main> uses "eager" `custom-app-id` namespace.
1489+ // <div>s inside a defer block have `d0` as a namespace.
1490+ expect ( ssrContents ) . toContain ( '<article id="item-1" jsaction="click:;keydown:;"' ) ;
1491+ // Outer defer block is rendered.
1492+ expect ( ssrContents ) . toContain ( 'defer block 1 rendered' ) ;
1493+
1494+ // Internal cleanup before we do server->client transition in this test.
1495+ resetTViewsFor ( SimpleComponent ) ;
1496+
1497+ ////////////////////////////////
1498+ const doc = getDocument ( ) ;
1499+ const appRef = await prepareEnvironmentAndHydrate ( doc , html , SimpleComponent , {
1500+ envProviders : [ ...providers , { provide : PLATFORM_ID , useValue : 'browser' } ] ,
1501+ hydrationFeatures,
1502+ } ) ;
1503+ const compRef = getComponentRef < SimpleComponent > ( appRef ) ;
1504+ appRef . tick ( ) ;
1505+ await whenStable ( appRef ) ;
1506+
1507+ const appHostNode = compRef . location . nativeElement ;
1508+
1509+ expect ( appHostNode . outerHTML ) . toContain ( '<article id="item-1" jsaction="click:;keydown:;"' ) ;
1510+
1511+ // Emit an event inside of a defer block, which should result
1512+ // in triggering the defer block (start loading deps, etc) and
1513+ // subsequent hydration.
1514+ const article = doc . getElementById ( 'item-1' ) ! ;
1515+ const clickEvent = new CustomEvent ( 'click' , { bubbles : true } ) ;
1516+ article . dispatchEvent ( clickEvent ) ;
1517+ await timeout ( 1000 ) ; // wait for defer blocks to resolve
1518+
1519+ appRef . tick ( ) ;
1520+ expect ( appHostNode . outerHTML ) . not . toContain (
1521+ '<article id="item-1" jsaction="click:;keydown:;"' ,
1522+ ) ;
1523+ expect ( appHostNode . outerHTML ) . not . toContain ( '<span>Outer block placeholder</span>' ) ;
1524+ } ) ;
1525+ } ) ;
1526+
14481527 describe ( 'cleanup' , ( ) => {
14491528 it ( 'should cleanup partial hydration blocks appropriately' , async ( ) => {
14501529 @Component ( {
@@ -1585,7 +1664,6 @@ describe('platform-server partial hydration integration', () => {
15851664
15861665 await timeout ( 1000 ) ; // wait for defer blocks to resolve
15871666
1588- appRef . tick ( ) ;
15891667 expect ( registry . size ) . toBe ( 1 ) ;
15901668 expect ( registry . has ( 'd0' ) ) . toBeFalsy ( ) ;
15911669 expect ( jsActionMap . size ) . toBe ( 1 ) ;
0 commit comments