I'm using that example to setup Angular 4 Universal with nodejs & expressjs : https://github.com/FrozenPandaz/ng-universal-demo
I've been trying to pass data from the first full page load to the Angular 4 final app.
The use case would be to transfer the language detected from the headers to absorb it from the Angular 4 server renderer so it can use the correct language to translate things.
The only way I've found so far is to do the language detection using HTTP headers from expressjs at the "/" route and redirect with a HTTP 302 to the "/fr" for example if the user language is French.
main.server.ts
app.param('language', function(req: any, res, next, language) {
if(language.length == 2)
{
req.language = language;
next();
} else {
next(new Error('Invalid language found'));
}
});
// Redirect incoming trafic without language in url (302 used so the browser do not seal that redirection if the user decides to change language and reload)
app.get('/', function(req, res) {
res.set('location', baseUrl + '/' + getLanguage(req);
res.status(302).send();
});
routes.ts
export const ROUTES: string[] = [
'/error',
'/:language',
'/:language/download'
];
It works, because I'm able to configure the Angular 4 router and retrieve the current route, but it's kind of dirty.
app.module.ts
RouterModule.forRoot([
{ path: '', component: HomeView, pathMatch: 'full'},
{ path: ':language/download', loadChildren: './+lazy/lazy.module#LazyModule'},
{ path: ':language/', redirectTo: ''},
// Catch all if expressjs allowed this url
{ path: '**', component: HomeView }
])
Is there a better way than URL params to pass data from ExpressJS to the Angular 4 app using Angular Universal?
I had the same problem and after several hours of searching I found a solution that is working for me here.
In your 'main.server.ts' file, you have to add 'extraProviders' to the app.engine()
function that look like so:
...
let template = readFileSync(join(__dirname, '..', 'dist', 'index.html')).toString();
app.engine('html', (_, options, callback) => {
const opts = {
document: template
,url: options.req.url
,extraProviders: [
{ provide: 'host', useFactory: () => options.req.get('host') }
,{ provide: 'PLACEHOLDER', useFactory: () => 'SOME PLACEHOLDER TEXT' }
]
};
renderModuleFactory(AppServerModuleNgFactory, opts)
.then(html => callback(null, html));
});
app.set('view engine', 'html');
...
You can then access the providers that you have defined in the 'main.server.ts' file in your Angular4-component or -service code like so:
import { Injectable, Injector, PLATFORM_ID } from '@angular/core';
import { isPlatformServer } from '@angular/common';
@Injectable()
export class I18nService {
constructor(
private injector: Injector
,@Inject(PLATFORM_ID) private platformId: Object
){
let host = null;
if (isPlatformServer(this.platformId)) {
// -> server rendered
host = this.injector.get('host');
//do something with host here
} else {
// -> browser rendered
host = document.location...
}
console.log(host);
//do something clever with 'host' here
}
...
Please note that once your page has loaded in the client-side-browser the 'host'-provider will no longer be available. Instead you have to get the host from the JS api.
Here is my complete 'main.server.ts' file:
import 'reflect-metadata';
import 'zone.js/dist/zone-node';
import { platformServer, renderModuleFactory } from '@angular/platform-server'
import { enableProdMode } from '@angular/core'
import { AppServerModuleNgFactory } from '../dist/ngfactory/src/app/app.server.module.ngfactory'
import * as express from 'express';
import { readFileSync } from 'fs';
import { join } from 'path';
import * as compression from 'compression';
const PORT = 4200;
enableProdMode();
const app = express();
let template = readFileSync(join(__dirname, '..', 'dist', 'index.html')).toString();
app.engine('html', (_, options, callback) => {
const opts = {
document: template
,url: options.req.url
,extraProviders: [
{ provide: 'request', useFactory: () => options.req }
,{ provide: 'host', useFactory: () => options.req.get('host') }
]
};
renderModuleFactory(AppServerModuleNgFactory, opts)
.then(html => callback(null, html));
});
app.set('view engine', 'html');
app.set('views', 'src');
app.use(compression());
app.get('*.*', express.static(join(__dirname, '..', 'dist')));
app.get('*', (req, res) => {
res.render('index', { req });
});
app.listen(PORT, () => {
console.log(`listening on http://localhost:${PORT}!`);
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With