Angular universal only for specific modules
Asked Answered
L

2

6

I have an Angular web app (client - Angular 9, server - Java), and now I want to add new components to be rendering on the server (e.g. Express-engine) using Angular universal.

My goal is to keep running the all app as regular in browser and only the specific module of the new components will render on the server. The website should be loaded as usual, and when routing to specific part of the app it will load the pre-render module from the server.

Is it possible to render only part of the web on the server?

Thanks!

EDIT: As @izmaylovdev suggest, I tried to edit the server.ts with the below get:

// All regular routes use the Universal engine
server.get('*', (req, res) => {     
  res.sendFile(distFolder + '\\' + indexHtml);   
}); 
// Specific route
server.get(matcherForSpecificRoutes, (req, res) => {     
  res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });   
});

But how can I see the different behavior? I can see a different log only on serve to the different URLs.

Lepore answered 6/8, 2020 at 11:25 Comment(1)
Does this answer your question? Is there a way to combine CSR and SSR for the same Angular app?Neutrino
E
4

SSR allows you to return rendered page from server for any route of your app. But after load of JS scripts your app works as usual (with client-side rendering). If you don't want to use SSR for specific routes, you can setup your server (or proxy) for return index.html (from dist folder) for these routes, and use SSR for other routes, in server setup Angular Universal it's just a template engine.

For your case you can configure Express like that:

export function app() {
  const server = express();
  const distFolder = join(process.cwd(), 'path to dist folder');
  const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';


  // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
  server.engine('html', ngExpressEngine({
    bootstrap: AppServerModule,
  }));

  server.set('view engine', 'html');
  server.set('views', distFolder);

  // Example Express Rest API endpoints
  // app.get('/api/**', (req, res) => { });
  // Serve static files from /browser
  server.get('**/*.*', express.static(distFolder, {
    maxAge: '1y'
  }));
      
  server.get('*', (reg, res) => { res.send(indexHtml) });

  server.get(matcherForSpecificRoutes, (req, res) => {
    global['navigator'] = req['headers']['user-agent'];
    const http = req.headers['x-forwarded-proto'] === undefined ? 'http' : req.headers['x-forwarded-proto'];

    const url = req.originalUrl;
    // tslint:disable-next-line:no-console
    console.time(`GET: ${url}`);

    res.render(indexHtml, {
      req,
      providers: [
        { provide: APP_BASE_HREF, useValue: req.baseUrl },
        {
          provide: REQUEST,
          useValue: req
        },
        {
          provide: RESPONSE,
          useValue: res     
        },
        {
          provide: 'ORIGIN_URL',
          useValue: (`${http}://${req.headers.host}`)
        }
      ]
    });
  });

  return server;
}
Epi answered 6/8, 2020 at 11:36 Comment(2)
This is the point. I want to make the opposite. To return the res.render just for specific routes.Lepore
Thanks @izmaylovdev, but it's not worked, maybe I'm missing something? Please see my edit in the question.Lepore
N
1

For non ssr route you could easily do like this. Just make changes on server.ts file like below.

  // non ssr routes like dashboard or auth pages
  server.get('/dashboard/**', (req, res) => {
    console.log('not rendering auth page');
    res.sendFile(distFolder + '/index.html');
  });

  // All regular routes use the Universal engine
  server.get('*', (req, res) => {
    res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
  });

server.ts

(global as any).WebSocket = require('ws');
(global as any).XMLHttpRequest = require('xhr2');
import 'zone.js/dist/zone-node';

const domino = require('domino');
const fs = require('fs');
const path = require('path');


import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';
import { existsSync } from 'fs';

const distFolder = join(process.cwd(), 'dist/localmasters/browser');
const template = fs.readFileSync(path.join(distFolder, 'index.html')).toString();
const win = domino.createWindow(template.toString());
global['window'] = win;
global['document'] = win.document;
global['DOMTokenList'] = win.DOMTokenList;
global['Node'] = win.Node;
global['Text'] = win.Text;
global['HTMLElement'] = win.HTMLElement;
global['navigator'] = win.navigator;
global['getComputedStyle'] = win.getComputedStyle;

(global as any).self = {fetch: require('node-fetch')};



import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '@angular/common';
import { environment } from './src/environments/environment';

// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
  const server = express();
  const distFolder = join(process.cwd(), 'dist/localmasters/browser');
  const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';

  // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
  server.engine('html', ngExpressEngine({
    bootstrap: AppServerModule,
  }));

  server.set('view engine', 'html');
  server.set('views', distFolder);

  // Example Express Rest API endpoints
  // server.get('/api/**', (req, res) => { });
  // Serve static files from /browser
  server.get('*.*', express.static(distFolder, {
    maxAge: '1y'
  }));
  server.get('/dashboard/**', express.static(distFolder, {
    maxAge: '1y'
  }));
  server.get('/dashboard/**', (req, res) => {
    console.log('not rendering auth page');
    res.sendFile(distFolder + '/index.html');
  });
  // All regular routes use the Universal engine
  server.get('*', (req, res) => {
    res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
  });

  return server;
}

function run(): void {
  const port = process.env.PORT || environment.PORT;

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

// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
  run();
}

export * from './src/main.server';

Nervy answered 6/12, 2021 at 7:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.