Skip to content

Commit d687967

Browse files
AndrewKushniralxhub
authored andcommitted
fix(core): handle hydration of root components with injected ViewContainerRef (#50136)
This commit fixes an issue where a root component with an injected ViewContainerRef (for ex. `inject(ViewContainerRef)`) was triggering a certain code path during hydration which didn't handle this case correctly. Resolves #50133. PR Close #50136
1 parent d0023d9 commit d687967

File tree

2 files changed

+39
-2
lines changed

2 files changed

+39
-2
lines changed

packages/core/src/hydration/utils.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import {Injector} from '../di/injector';
1111
import {ViewRef} from '../linker/view_ref';
1212
import {getDocument} from '../render3/interfaces/document';
1313
import {RElement, RNode} from '../render3/interfaces/renderer_dom';
14-
import {isRootView} from '../render3/interfaces/type_checks';
15-
import {HEADER_OFFSET, LView, TVIEW, TViewType} from '../render3/interfaces/view';
14+
import {isLContainer, isRootView} from '../render3/interfaces/type_checks';
15+
import {HEADER_OFFSET, HOST, LView, TVIEW, TViewType} from '../render3/interfaces/view';
1616
import {makeStateKey, TransferState} from '../transfer_state';
1717
import {assertDefined} from '../util/assert';
1818

@@ -142,6 +142,13 @@ export function getComponentLViewForHydration(viewRef: ViewRef): LView|null {
142142
if (isRootView(lView)) {
143143
lView = lView[HEADER_OFFSET];
144144
}
145+
146+
// If a `ViewContainerRef` was injected in a component class, this resulted
147+
// in an LContainer creation at that location. In this case, the component
148+
// LView is in the LContainer's `HOST` slot.
149+
if (isLContainer(lView)) {
150+
lView = lView[HOST];
151+
}
145152
return lView;
146153
}
147154

packages/platform-server/test/hydration_spec.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1523,6 +1523,36 @@ describe('platform-server integration', () => {
15231523
verifyAllNodesClaimedForHydration(clientRootNode, exceptions);
15241524
verifyClientAndSSRContentsMatch(ssrContents, clientRootNode);
15251525
});
1526+
1527+
it('should allow injecting ViewContainerRef in the root component', async () => {
1528+
@Component({
1529+
standalone: true,
1530+
selector: 'app',
1531+
template: `Hello World!`,
1532+
})
1533+
class SimpleComponent {
1534+
private vcRef = inject(ViewContainerRef);
1535+
}
1536+
1537+
const html = await ssr(SimpleComponent);
1538+
const ssrContents = getAppContents(html);
1539+
1540+
expect(ssrContents).toContain(`<app ${NGH_ATTR_NAME}`);
1541+
1542+
resetTViewsFor(SimpleComponent);
1543+
1544+
const appRef = await hydrate(html, SimpleComponent);
1545+
const compRef = getComponentRef<SimpleComponent>(appRef);
1546+
appRef.tick();
1547+
1548+
const clientRootNode = compRef.location.nativeElement;
1549+
verifyAllNodesClaimedForHydration(clientRootNode);
1550+
1551+
// Replace the trailing comment node (added as a result of the
1552+
// `ViewContainerRef` injection) before comparing contents.
1553+
const _ssrContents = ssrContents.replace(/<\/app><!--container-->/, '</app>');
1554+
verifyClientAndSSRContentsMatch(_ssrContents, clientRootNode);
1555+
});
15261556
});
15271557

15281558
describe('<ng-template>', () => {

0 commit comments

Comments
 (0)