Reputation: 1139
Background: Using Angular Universal to perform pre-rendering but not all routes will be rendered (query parametered or authenticated-only pages for the most part), so wanting to fallback to the express renderer as needed.
Quick Replication (bash):
npm install -g @angular/cli@next
ng new partial-prerender -s -t --minimal --routing --interactive=false
cd partial-prerender/
ng add @nguniversal/express-engine@'^9.0.0-rc.1'
ng g m child --route child --module app
cat << 'EOF' > src/app/app.component.ts
import { Component } from '@angular/core';
@Component({
template: `<div [routerLink]="['/']">Root</div><div [routerLink]="['child']">Child</div><router-outlet></router-outlet>`,
})
export class AppComponent {}
EOF
npm run prerender
npm run serve:ssr
This quick replication will produce the app, universal implementation, a child page, and replace the app html to give 2 links and a router outlet, then build/pre-render. Both routes will be pre-rendered, but this is good enough for discussing the issue.
Problem: Dynamic SSR is performed as the Express server will pick up the request rather than serving the pre-rendered static file. URLs are normally accessed without the /index.html
specified.
Note the static files can be found at /dist/partial-prerender/browser/index.html
and .../child/index.html
. For testing, I've replaced the contents of these files with garbage, just to be sure which is being loaded at a glance.
Can also add a console.log('DYNAMIC');
to the server.ts
:
server.get('*', (req, res) => {
console.log('DYNAMIC');
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
When making a request to localhost:4000
or localhost:4000/child
, the 'DYNAMIC' will be printed and the dynamically rendered version is produced, not giving me my mangled pre-rendered files.
When making a request to localhost:4000/index.html
or localhost:4000/child/index.html
, the
server.get('*.*', express.static(distFolder, { maxAge: '1y' }));
picks up and serves the mangled files.
All makes sense and why it's happening, but I want to be able to just hit a given url (without the /index.html
and receive the pre-rendered files (when available), then fall back to putting SSR to work.
Potential Solution:
Modify server.ts
to test for file existence matching the given request path + /index.html
and serve them, falling back to the res.render(...
express.static
should be utilized in some way over fs
fs
is the answer, would it make sense to cache the pre-rendered files in memory?If it helps, I produce an alpine-node container and deploy to K8s with an Nginx ingress. Only mention this as maybe there's a magical try-files
-like functionality that can be done to 'attempt' a file + /index.html
retrieval from the node container, then fallback without the /index.html
, but seems highly unlikely.
Upvotes: 4
Views: 4609
Reputation: 1
npm install @nguniversal/[email protected] --save
It already brings the static page by default.
Upvotes: -1
Reputation: 275
You could use an if statement inside the get request like this
const fullPath = join(distFolder, req.originalUrl);
if (existsSync(fullPath)) {
console.log('STATIC Exists');
return res.sendFile(join(distFolder, req.originalUrl));
} else {
//Dynamic
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
}
Upvotes: 4