diff --git a/docs/upgrades/unreleased.md b/docs/upgrades/unreleased.md index 028340885e..087516275b 100644 --- a/docs/upgrades/unreleased.md +++ b/docs/upgrades/unreleased.md @@ -47,43 +47,77 @@ } ``` -* Update jss-translation-client-loader service to get the performance improvement during fetching Dictionary Data for SSR - * In `/src/app/i18n/jss-translation-client-loader.service.ts` - * Add import for TranferState and of - ``` - import { TransferState } from '@angular/core'; - import { of } from 'rxjs'; - ``` - * Update `dictionaryStateKey` variable type - ``` - export const dictionaryStateKey = makeStateKey<{ [key: string]: string }>('jssDictionary'); - ``` - * Add `transferState` variable to constructor - ``` - constructor(private fallbackLoader: TranslateLoader, private transferState: TransferState) {} - ``` - * Update the `getTranslation` method - ``` - getTranslation(lang: string) { - const storedDictionary = this.transferState.get<{ [key: string]: string } | null>( - dictionaryStateKey, - null - ); - - if (storedDictionary !== null && Object.keys(storedDictionary).length > 0) { - return of(storedDictionary); - } - - ... - } - ``` - * Update `/src/templates/angular/src/app/app.module.ts` - ``` +* Update i18n initialization to gain the performance improvement during fetching Dictionary Data for using SSR: + * Inject _TransferState_ both on the server and client side: + + `app.module.ts`: + + ```ts + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useFactory: (transferState: TransferState) => + new JssTranslationClientLoaderService(new JssTranslationLoaderService(), transferState), + deps: [TransferState], + }, + }), + ``` + + `app.server.module.ts`: + + ```ts + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useFactory: ( + ssrViewBag: { + [key: string]: unknown; + dictionary: { [key: string]: string }; + }, + transferState: TransferState + ) => new JssTranslationServerLoaderService(ssrViewBag, transferState), + deps: ['JSS_SERVER_VIEWBAG', TransferState], + }, + }), + ``` + + * In `app\i18n\jss-translation-server-loader.service.ts`: + * Inject _TransferState_. + * Make sure to set _dictionary_ data in _transferState_. + + ```ts + export const dictionaryStateKey: StateKey = makeStateKey( + 'dictionary' + ); + + ... + + getTranslation(_lang: string) { + const dictionary = this.serverViewBag.dictionary; + + this.transferState.set(dictionaryStateKey, dictionary); ... - useFactory: (transferState: TransferState) => - new JssTranslationClientLoaderService(new JssTranslationLoaderService(), transferState), + } + ``` + + * In `app\i18n\jss-translation-client-loader.service.ts`: + * Inject _TransferState_. + * Make sure to check for _dictionary_ data in _transferState_ and use it if available and provided by the server. + + ```ts + import { dictionaryStateKey } from './jss-translation-server-loader.service'; + + ... + + getTranslation(lang: string): Observable { + const dictionary = this.transferState.get(dictionaryStateKey, null); + + if (dictionary) { + return of(dictionary); + } ... - ``` + } + ``` # Angular - XMCloud diff --git a/packages/create-sitecore-jss/src/templates/angular/src/app/app.module.ts b/packages/create-sitecore-jss/src/templates/angular/src/app/app.module.ts index 9ca183bbf8..80e121774d 100644 --- a/packages/create-sitecore-jss/src/templates/angular/src/app/app.module.ts +++ b/packages/create-sitecore-jss/src/templates/angular/src/app/app.module.ts @@ -1,6 +1,6 @@ import { APP_ID, NgModule, TransferState } from '@angular/core'; import { APP_BASE_HREF } from '@angular/common'; -import { HttpClientModule, HttpClient } from '@angular/common/http'; +import { HttpClientModule } from '@angular/common/http'; import { RoutingModule } from './routing/routing.module'; import { JssLayoutService } from './layout/jss-layout.service'; import { AppComponentsModule } from './components/app-components.module'; @@ -22,7 +22,7 @@ import { JssContextService } from './jss-context.service'; provide: TranslateLoader, useFactory: (transferState: TransferState) => new JssTranslationClientLoaderService(new JssTranslationLoaderService(), transferState), - deps: [HttpClient, TransferState], + deps: [TransferState], }, }), AppComponentsModule, diff --git a/packages/create-sitecore-jss/src/templates/angular/src/app/app.server.module.ts b/packages/create-sitecore-jss/src/templates/angular/src/app/app.server.module.ts index cdec17e7e7..7c601269d9 100644 --- a/packages/create-sitecore-jss/src/templates/angular/src/app/app.server.module.ts +++ b/packages/create-sitecore-jss/src/templates/angular/src/app/app.server.module.ts @@ -1,4 +1,4 @@ -import { NgModule } from '@angular/core'; +import { NgModule, TransferState } from '@angular/core'; import { ServerModule } from '@angular/platform-server'; import { TranslateModule, TranslateLoader } from '@ngx-translate/core'; @@ -18,11 +18,14 @@ import { JssTranslationServerLoaderService } from './i18n/jss-translation-server // <-- *Important* to get translation values server-side loader: { provide: TranslateLoader, - useFactory: (ssrViewBag: { - [key: string]: unknown; - dictionary: { [key: string]: string }; - }) => new JssTranslationServerLoaderService(ssrViewBag), - deps: ['JSS_SERVER_VIEWBAG'], + useFactory: ( + ssrViewBag: { + [key: string]: unknown; + dictionary: { [key: string]: string }; + }, + transferState: TransferState + ) => new JssTranslationServerLoaderService(ssrViewBag, transferState), + deps: ['JSS_SERVER_VIEWBAG', TransferState], }, }), ], diff --git a/packages/create-sitecore-jss/src/templates/angular/src/app/i18n/jss-translation-client-loader.service.ts b/packages/create-sitecore-jss/src/templates/angular/src/app/i18n/jss-translation-client-loader.service.ts index f8c992c657..1ed7d831d2 100644 --- a/packages/create-sitecore-jss/src/templates/angular/src/app/i18n/jss-translation-client-loader.service.ts +++ b/packages/create-sitecore-jss/src/templates/angular/src/app/i18n/jss-translation-client-loader.service.ts @@ -1,21 +1,22 @@ -import { makeStateKey, Injectable, TransferState } from '@angular/core'; +import { Injectable, TransferState } from '@angular/core'; import { TranslateLoader } from '@ngx-translate/core'; -import { EMPTY, of } from 'rxjs'; - -export const dictionaryStateKey = makeStateKey<{ [key: string]: string }>('jssDictionary'); +import { DictionaryPhrases } from '@sitecore-jss/sitecore-jss-angular'; +import { EMPTY, Observable, of } from 'rxjs'; +import { JssTranslationLoaderService } from './jss-translation-loader.service'; +import { dictionaryStateKey } from './jss-translation-server-loader.service'; @Injectable() export class JssTranslationClientLoaderService implements TranslateLoader { - constructor(private fallbackLoader: TranslateLoader, private transferState: TransferState) {} + constructor( + private fallbackLoader: JssTranslationLoaderService, + private transferState: TransferState + ) {} - getTranslation(lang: string) { - const storedDictionary = this.transferState.get<{ [key: string]: string } | null>( - dictionaryStateKey, - null - ); + getTranslation(lang: string): Observable { + const dictionary = this.transferState.get(dictionaryStateKey, null); - if (storedDictionary !== null && Object.keys(storedDictionary).length > 0) { - return of(storedDictionary); + if (dictionary) { + return of(dictionary); } if (!this.fallbackLoader) { diff --git a/packages/create-sitecore-jss/src/templates/angular/src/app/i18n/jss-translation-server-loader.service.ts b/packages/create-sitecore-jss/src/templates/angular/src/app/i18n/jss-translation-server-loader.service.ts index ad0992406b..78179d76c4 100644 --- a/packages/create-sitecore-jss/src/templates/angular/src/app/i18n/jss-translation-server-loader.service.ts +++ b/packages/create-sitecore-jss/src/templates/angular/src/app/i18n/jss-translation-server-loader.service.ts @@ -1,17 +1,29 @@ -import { Inject, Injectable } from '@angular/core'; +import { Inject, Injectable, makeStateKey, StateKey, TransferState } from '@angular/core'; import { TranslateLoader } from '@ngx-translate/core'; +import { DictionaryPhrases } from '@sitecore-jss/sitecore-jss-angular'; import { of as observableOf, EMPTY } from 'rxjs'; +export const dictionaryStateKey: StateKey = makeStateKey( + 'dictionary' +); + @Injectable() export class JssTranslationServerLoaderService implements TranslateLoader { constructor( // this initial state from sitecore is injected by server.bundle for "integrated" mode @Inject('JSS_SERVER_VIEWBAG') - private serverViewBag: { [key: string]: unknown; dictionary: { [key: string]: string } } + private serverViewBag: { [key: string]: unknown; dictionary: DictionaryPhrases }, + private transferState: TransferState ) {} getTranslation(_lang: string) { // read initial dictionary from data injected via server.bundle wrapper const dictionary = this.serverViewBag.dictionary; + + // set the dictionary in transfer state for the client + // since for ng-translate there is no obvious way to pass the server side dictionary to the client + // https://github.com/ngx-translate/core/issues/1207#issuecomment-700741671 + this.transferState.set(dictionaryStateKey, dictionary); + if (dictionary) { return observableOf(dictionary); } diff --git a/packages/sitecore-jss-angular/src/public_api.ts b/packages/sitecore-jss-angular/src/public_api.ts index 5cfae981fa..f08091f011 100644 --- a/packages/sitecore-jss-angular/src/public_api.ts +++ b/packages/sitecore-jss-angular/src/public_api.ts @@ -42,6 +42,7 @@ export { DictionaryService, GraphQLDictionaryService, RestDictionaryService, + DictionaryPhrases, } from '@sitecore-jss/sitecore-jss/i18n'; export { LayoutService,