node.jsangularangular8angular-universaluniversal

SSR angular universal and angular 8 - rendering dynamic html content before api completes


I'm able to dynamically render html for server side rendering using angular 8 and angular universal when I view page source BUT my issue is that I am rendering the dynamic html before the api request has finished loading so I can't see the response in the dynamic html from the view page source - please let me know if you require any further info. I run this command: i.e. npm run build:ssr && npm run serve:ssr and when I look at the logs and change the url I get a [NetworkError] in the console but the app still runs as expected, I need to figure out a way to load the api response and then render the html into the page view source after the request has finished but I've run out of ideas.

Hopefully one of you guys can help, Thanks

Server.ts

import 'zone.js/dist/zone-node';
import 'localstorage-polyfill';
import { join } from 'path';
import * as express from 'express';

const compression = require('compression');
const sessionStorage = require('sessionstorage');
const DIST_FOLDER = join(process.cwd(), 'dist');
const domino = require('domino');
const fs = require('fs');
const template = fs.readFileSync('./dist/browser/index.html').toString();
const win = domino.createWindow(template);
const proxy = require('http-proxy-middleware');
const cors = require('cors');
const helmet = require('helmet');



Object.assign(global, domino.impl);
(global as any)['KeyboardEvent'] = domino.impl.Event;
global['window'] = win;
global['Node'] = win.Node;
global['navigator'] = win.navigator;
global['Event'] = win.Event;
global['KeyboardEvent'] = win.Event;
global['MouseEvent'] = win.Event;
global['Event']['prototype'] = win.Event.prototype;
global['document'] = win.document;
global['sessionStorage'] = sessionStorage;
global['localStorage'] = localStorage;


// Express server
const app = express();

const PORT = process.env.PORT || 4200;
const {AppServerModuleNgFactory, LAZY_MODULE_MAP, ngExpressEngine, provideModuleMap} = require('./dist/server/main');
app.use(cors());
app.use(compression());


// express-engine
app.engine('html', ngExpressEngine({
    bootstrap: AppServerModuleNgFactory,
    providers: [
        provideModuleMap(LAZY_MODULE_MAP)
    ]
}));

app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'browser'));

// Server static files from /browser
app.get('*.*', express.static(join(DIST_FOLDER, 'browser')));

// Protect website from Clickjacking attack
app.use(helmet.frameguard());
app.use(helmet.xssFilter());

//  Proxy API Endpoints


app.use('/api/profileProxy', proxy(
  {
      target: 'http://xxxxxxx1:9004', // target host
      changeOrigin: true, // needed for virtual hosted sites
      // ws: true, // proxy websockets
      pathRewrite: {
          '^/api/profileProxy': ''
      }
  }
));

app.use('/api/searchProxy', proxy(
  {
      target: 'http://xxxxxx.160:9005', // target host
      changeOrigin: true, // needed for virtual hosted sites
      // ws: true, // proxy websockets
      pathRewrite: {
          '^/api/searchProxy': ''
      }
  }
));



app.get('/sitemap1.xml', function (req, res, next) {
  const file = `${DIST_FOLDER}/sitemap1.xml`;

  fs.exists(file, function (exists) {
    if (exists) {
      res.sendFile(file);
    } else {
      res.status(404).send('404');
    }
  });
});

app.get('/robots.txt', function (req, res, next) {
  const file = `${DIST_FOLDER}/robots.txt`;

  fs.exists(file, function (exists) {
      if (exists) {
          res.sendFile(file);
      } else {
          res.status(404).send('404');
      }
  });
});

// All regular routes use the Universal engine

app.get('*', (req, res) => {
    console.time(`GET: ${req.originalUrl}`);
    console.log(`req-QQQQQQQQQQ: ${req.originalUrl}`);
    res.render(join(DIST_FOLDER, 'browser', 'index.html'), { req });
    console.timeEnd(`GET: ${req.originalUrl}`);
    console.log(`req-timeEnd: ${req.originalUrl}`);
});

// Start up the Node server

app.listen(PORT, () => {
  console.log(`Node Express server listening on http://localhost:${PORT}`);
});

``


Solution

  • for people that may come across this issue - I was able to display the data from the api request (in my case the initial search results) in the view page source for SSR by making a GET request in the app.component of my app - thanks

    enter image description here