Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2 Routing in conjunction to Express routing?

My angular2 app's routes don't work when accessed via URL... Express is rendering an error page instead.

So I have one route (/docs) which serves some static content and some other static resources, however, / is routed to an index.html which is managed by angular 2. So by opening the application root and then clicking various router links I can get to a route e.g. /tutorial/chapter/1. However, as that isn't a registered route in my express app, if I refresh the page I get a 404.

I want to be able to type http://localhost:3000/tutorial/chapter/1 into my browser and get that page. How do I set express to route all undefined routes to angular, and let angular handle the 404?

Here is my app.js:

var app = express();

// html view engine setup
app.set('views', path.join(__dirname, '/ng2/views'));
app.engine('html', require('jade').renderFile);
app.set('view engine', 'html');

app.use(express.static('ng2/views'));
app.use(express.static('ng2/public'));

app.use('/node_modules', express.static(__dirname + '/node_modules'));    

// uncomment after placing your favicon in /public
app.use(favicon(path.join(__dirname, 'ng2/public', 'favicon.png')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());

//all static assetes for hexo content

app.use('/docs', serveStatic('features/docs/public', { 'index': ['index.html', 'index.htm'] }));

app.use('/', routes);

// catch 404 and forward to error handler
app.use(function (req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});


module.exports = app;

You can see the full repo here

Here is the routes middleware def:

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});


module.exports = router;
like image 214
George Edwards Avatar asked Aug 17 '16 21:08

George Edwards


People also ask

Can we use multiple routes in one application?

js allows us to create multiple routes on a single express server. Creating multiple routes on a single server is better to practice rather than creating single routes for handling different requests made by the client.

What is routing in angular2?

Routing helps in directing users to different pages based on the option they choose on the main page. Hence, based on the option they choose, the required Angular Component will be rendered to the user. Let's see the necessary steps to see how we can implement routing in an Angular 2 application.

What is angular8 routing?

The process of defining navigation element and the corresponding view is called Routing. Angular provides a separate module, RouterModule to set up the navigation in the Angular application.


3 Answers

Angular 2 assumes that independent of the request URL, the frontend will be returned. This assumption is based on a feature modern browsers implement called push state. You have 3 options if you want to support anything but the bleeding edge of browsers:

  • Recommended: Seperate the API server from the client.
    If you put your client on example.org and your express backend on api.example.org you can just do what Angular assumes to be true. You can also deploy independently and the client can live on a static host or CDN. This will require that you setup CORS though.

  • Catch-All Express Route
    Make sure all your routes in Express differ from the ones you setup in NG2 and make a catch-all handler. Put something like this at the end of your routes/middleware but before the 404 handler!

    app.use(function(req, res, next) {
      res.sendFile("index.html");
    })
    
  • Use legacy browser-url-styles for the router.
    You can make the NG2 router use hashes for routes. Check here.

like image 131
Aurelia Avatar answered Oct 21 '22 10:10

Aurelia


app.js

Since order is important and new code is inserted in multiple locations, the whole file is included. Look for comment started with // JS -

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var serveStatic = require('serve-static')
var file = require('./features/prepareTutorial');
var routes = require('./ng2/routes/index');

var app = express();

// html view engine setup
app.set('views', path.join(__dirname, '/ng2/views'));
app.engine('html', require('jade').renderFile);
app.set('view engine', 'html');

app.use(express.static('ng2/views'));
app.use(express.static('ng2/public'));

app.use('/node_modules', express.static(__dirname + '/node_modules'));
app.use('/persist', express.static(__dirname + '/persist'));

// JS - Add /app
app.use('/app', express.static(__dirname + '/ng2/views/app'));

// I have to comment this line because it failed
//file.processTutorial(); //generate html rendered patches for tutorial steps
//file.genGit(); //generate git SHA
file.processChapters();

// uncomment after placing your favicon in /public
app.use(favicon(path.join(__dirname, 'ng2/public', 'favicon.png')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());

//all static assetes for hexo content

app.use('/docs', serveStatic('features/docs/public', { 'index': ['index.html', 'index.htm'] }));
//app.use(subdomain('docs', express.static('docs/public')));
app.use('/script', serveStatic('features/docs/public/script'));
app.use('/style', serveStatic('features/docs/public/style'));
app.use('/images', serveStatic('features/docs/public/images'));
app.use('/diff', serveStatic('features/tutorial/diffs'));
app.use('/git', serveStatic('features/git'));
app.use('/chapter', serveStatic('ng2/views/app/tutorial/chapter/chapters'));
app.use('/img', serveStatic('features/docs/source/img'));

app.use('/config', serveStatic('ng2/config'));

app.use('/', routes);

// JS - /tutorial static
//app.use('/tutorial', express.static('ng2/views/app/tutorial'));
// JS - /tutorial/chapter/* send index file 
app.all(/^\/tutorial$/, (req, res) => {
  res.redirect('/tutorial/');
});
app.use('/tutorial/', (req, res) => {
  res.sendFile(__dirname + '/ng2/views/index.html');
});

// catch 404 and forward to error handler
app.use(function (req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function (err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function (err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});


module.exports = app;

ng2/config/systemjs.config.js & ng2/public/config/systemjs.config.js

Use absolute path

This is the main issue. With relative path, the browser is requesting files at tutorial/chapter/2/app/*, tutorial/chapter/2/node_modules/*, etc, and the app break down completely.

// snip ...
var map = {
    'app':                        '/app', // 'dist',
    '@angular':                   '/node_modules/@angular',
    'angular2-in-memory-web-api': '/node_modules/angular2-in-memory-web-api',
    'rxjs':                       '/node_modules/rxjs'
  };
// snip ...

ng2/views/index.html

Use absolute path

This won't stop the page from loading but a mess.

// snip ...
<link rel="stylesheet" href="/stylesheets/style.css">
// snip ...
like image 29
John Siu Avatar answered Oct 21 '22 09:10

John Siu


Instead of app.use('/', routes);, register a middleware that will always serve the index.html. Be cautious though, this can cause your app to return index.html even inside the /docs route.

Just use the middleware that renders the index page:

app.use(routes);

Make sure the routes middleware itself always renders the page, not only on / path.

var express = require('express');

/* render home page. */
var router = function(req, res, next) {
  res.render('index', { title: 'Express' });
};

module.exports = router;

Remove this the 404 handler (it should be automatic)

// catch 404 and forward to error handler
app.use(function (req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

And change the node_modules route to the following (because SystemJS relies on 404 responses during resolution):

var modules = express.Router();

modules.use(express.static(__dirname + '/node_modules'));
modules.use(function(req, res, next) {
    // Missing files inside node_modules must return 404
    // for the module loader to work
    res.sendStatus(404);
});


app.use('/node_modules', modules);  
like image 1
Tamas Hegedus Avatar answered Oct 21 '22 10:10

Tamas Hegedus