From 290a47d8427f4854234cb2b4244871eaf1f82c19 Mon Sep 17 00:00:00 2001 From: Andrew Kushnir Date: Wed, 29 May 2024 21:23:44 -0700 Subject: [PATCH] fix(core): handle missing `withI18nSupport()` call for components that use i18n blocks (#56175) This commit updates hydration serialization logic to handle a case when the `withI18nSupport()` call is not present for an application that has a component that uses i18n blocks. Note: the issue is only reproducible for components that also inject `ViewContainerRef`, since it triggers a special serialization code path. Resolves #56074. PR Close #56175 --- packages/core/src/hydration/annotate.ts | 8 +++++++ .../platform-server/test/hydration_spec.ts | 23 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/packages/core/src/hydration/annotate.ts b/packages/core/src/hydration/annotate.ts index 5d390e304a4..ddd8f56ec6e 100644 --- a/packages/core/src/hydration/annotate.ts +++ b/packages/core/src/hydration/annotate.ts @@ -182,6 +182,14 @@ function annotateLContainerForHydration(lContainer: LContainer, context: Hydrati // Serialize the root component itself. const componentLViewNghIndex = annotateComponentLViewForHydration(componentLView, context); + if (componentLViewNghIndex === null) { + // Component was not serialized (for example, if hydration was skipped by adding + // the `ngSkipHydration` attribute or this component uses i18n blocks in the template, + // but `withI18nSupport()` was not added), avoid annotating host element with the `ngh` + // attribute. + return; + } + const hostElement = unwrapRNode(componentLView[HOST]!) as HTMLElement; // Serialize all views within this view container. diff --git a/packages/platform-server/test/hydration_spec.ts b/packages/platform-server/test/hydration_spec.ts index b1dc17a383e..2582be4b200 100644 --- a/packages/platform-server/test/hydration_spec.ts +++ b/packages/platform-server/test/hydration_spec.ts @@ -1964,6 +1964,29 @@ describe('platform-server hydration integration', () => { clearTranslations(); }); + it('should append skip hydration flag if component uses i18n blocks and no `withI18nSupport()` call present', async () => { + @Component({ + standalone: true, + selector: 'app', + template: '
Hi!
', + }) + class SimpleComponent { + // Having `ViewContainerRef` here is important: it triggers + // a code path that serializes top-level `LContainer`s. + vcr = inject(ViewContainerRef); + } + + const hydrationFeatures = [] as unknown as HydrationFeature[]; + const html = await ssr(SimpleComponent, {hydrationFeatures}); + + const ssrContents = getAppContents(html); + + // Since `withI18nSupport()` was not included and a component has i18n blocks - + // we expect that the `ngSkipHydration` attribute was added during serialization. + expect(ssrContents).not.toContain('ngh="'); + expect(ssrContents).toContain('ngskiphydration="'); + }); + it('should not append skip hydration flag if component uses i18n blocks', async () => { @Component({ standalone: true,