Using Angular SSR, I would like to access a server-side value in my app.component.
Here is my server-side route:
app.get('**', (req, res, next) => {
const { protocol, originalUrl, baseUrl, headers } = req;
commonEngine
.render({
bootstrap,
documentFilePath: indexHtml,
url: `${protocol}://${headers.host}${originalUrl}`,
publicPath: browserDistFolder,
providers: [
{
provide: APP_BASE_HREF,
useValue: baseUrl
},
{
provide: 'message',
useValue: 'Message provided server-side'
}
],
})
.then((html) => res.send(html))
.catch((err) => next(err));
});
I am providing a value: message
Here's my component code, where I would like to pick up the message
value.
@Component({
imports: [
RouterOutlet,
],
selector: 'app-root',
styleUrl: './app.component.scss',
templateUrl: './app.component.html',
})
export class AppComponent {
constructor(
@Optional()
@Inject('message')
readonly message: string,
@Inject(PLATFORM_ID)
private readonly platformId: Object,
private transferState: TransferState,
) {
const MESSAGE_KEY = makeStateKey<string>('message');
if (isPlatformBrowser(this.platformId)) {
this.message = this.transferState.get(MESSAGE_KEY, 'Message provided client-side');
}
else {
console.log(this.message); // this value is null
this.transferState.set(MESSAGE_KEY, this.message);
}
}
}
message
is always null
.
Here's my app.config where I'm specifying client hydration:
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideClientHydration(withEventReplay())
]
};
Here's the app.config.server
const serverConfig: ApplicationConfig = {
providers: [
provideServerRendering(),
]
};
export const config = mergeApplicationConfig(appConfig, serverConfig);
What is the correct way to access provided values?
You need to add the providers at the app.config.ts, adding two different values for main.ts
(app.config.ts
) and main.server.ts
(app.config.server.ts).
import {
ApplicationConfig,
InjectionToken,
makeEnvironmentProviders,
provideZoneChangeDetection,
} from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideClientHydration } from '@angular/platform-browser';
export const MESSAGE = new InjectionToken<string>('message'); // <- this must be on the app.config.ts (!important)
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideClientHydration(),
{ provide: MESSAGE, useValue: 'message from the browser' },
],
};
import {
mergeApplicationConfig,
ApplicationConfig,
InjectionToken,
} from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';
import { appConfig, MESSAGE } from './app.config';
const serverConfig: ApplicationConfig = {
providers: [
provideServerRendering(),
{ provide: MESSAGE, useValue: 'message from the server' },
],
};
export const config = mergeApplicationConfig(appConfig, serverConfig);
import { Component, inject } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { MESSAGE } from './app.config';
import { PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet],
templateUrl: './app.component.html',
styleUrl: './app.component.scss',
})
export class AppComponent {
title = 'test';
message = inject(MESSAGE, {
optional: true,
});
platformId = PLATFORM_ID;
ngOnInit() {
if (isPlatformBrowser(this.platformId)) {
console.log('from browser', this.message);
} else {
console.log('from server', this.message);
}
}
}