In my Angular app I need to fetch different menus depending on the language selected (can't use i18n), and I'm currently using this code to do so:
protected readonly menuItems = signal<LibHeaderMenuItem[]>([]);
ngOnInit() {
import(`./menus/menus.${this.applicationManager.lang}.ts`).then(m => {
this.menuItems.set(m.default);
});
}
// NOTE: Greatly simplified for readability
@for (item of menuItems(); track item.href) {
<a [routerLink]="item.href">{{item.label}}</a>
}
Which works fine, with the problem that when rendered with SSR this will run both on the server and client, resulting in the menu flickering on initial load since Angular can't properly hydrate this data. If I only run the import code on the server, I get the data initially, but then it becomes completely empty once the JS is loaded on the client.
The only way I can think of to solve this is to use JSON files and load them with HttpClient
instead, but then I lose type safety in each menu file, and given the complexity of the data this would be quite risky.
Is there any way to make Angular properly hydrate data that comes from dynamic imports, just like they do with data from http requests?
EDIT:
I tried using the old TransferState
which seems to do the trick, but this feels a bit dirty, and something that would eventually get deprecated(?) since provideClientHydration
is supposed to take care of this for you.
In my Angular app I need to fetch different menus depending on the language selected (can't use i18n), and I'm currently using this code to do so:
protected readonly menuItems = signal<LibHeaderMenuItem[]>([]);
ngOnInit() {
import(`./menus/menus.${this.applicationManager.lang}.ts`).then(m => {
this.menuItems.set(m.default);
});
}
// NOTE: Greatly simplified for readability
@for (item of menuItems(); track item.href) {
<a [routerLink]="item.href">{{item.label}}</a>
}
Which works fine, with the problem that when rendered with SSR this will run both on the server and client, resulting in the menu flickering on initial load since Angular can't properly hydrate this data. If I only run the import code on the server, I get the data initially, but then it becomes completely empty once the JS is loaded on the client.
The only way I can think of to solve this is to use JSON files and load them with HttpClient
instead, but then I lose type safety in each menu file, and given the complexity of the data this would be quite risky.
Is there any way to make Angular properly hydrate data that comes from dynamic imports, just like they do with data from http requests?
EDIT:
I tried using the old TransferState
which seems to do the trick, but this feels a bit dirty, and something that would eventually get deprecated(?) since provideClientHydration
is supposed to take care of this for you.
provideClientHydration should work correctly if you request data via HttpClient.
I hope your menus.en.ts
can be menus.en.json
instead (i.e. it doesn't contain any nonserializable code).
then just put it to assets folder (or any other folder that will be copied to the build otput) and request it via httpClient
http = inject(HttpClient);
langs$ = this.http.get(`/assets/menus.${this.lang}.json`)