Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[template/angular] [follow-up] Prevent client-side dictionary API call when SSR data is available #1932

Merged
merged 2 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 69 additions & 35 deletions docs/upgrades/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<DictionaryPhrases> = makeStateKey<DictionaryPhrases>(
'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<DictionaryPhrases> {
const dictionary = this.transferState.get(dictionaryStateKey, null);

if (dictionary) {
return of(dictionary);
}
...
```
}
```

# Angular - XMCloud

Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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],
},
}),
],
Expand Down
Original file line number Diff line number Diff line change
@@ -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<DictionaryPhrases> {
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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<DictionaryPhrases> = makeStateKey<DictionaryPhrases>(
'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);
}
Expand Down
1 change: 1 addition & 0 deletions packages/sitecore-jss-angular/src/public_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export {
DictionaryService,
GraphQLDictionaryService,
RestDictionaryService,
DictionaryPhrases,
} from '@sitecore-jss/sitecore-jss/i18n';
export {
LayoutService,
Expand Down