Angular routing in HTML5mode with Node.js
Solution 1
You might be using the express middleware in the wrong way. Looking at the documentation tells me for example that the following code
.use( '/images', express.static( indexPath + '/images' ) )
Serves any file under the folder indexPath + '/images'
with such URLs:
http://localhost:3000/images/kitten.jpg
http://localhost:3000/images/logo.png
http://localhost:3000/images/whatever.jpg
Is that what you would expect it to do?
My suggestion is to change from
.use( '/', require( './routes/home' ) )
to
.use(express.static( indexPath ))
Because according to the documentation, it will serve all static files under the indexPath
folder from the root path, aka. with no prefix.
I think that is all I can help with the input you gave so far. If that doesn't do the trick, maybe you can share some small code example that I can use to reproduce it on my own.
UPDATE
Ok. I've tried to create a simple example of it. I made it work in the following way. My directory structure is like this:
|- index.js
|- public/
|---- index.html
|---- test.html
|---- main.js
|---- angular.js
|---- angular-route.js
index.js
is the node server. Pay attention to the order of the routing. I also added some /home/login
example to make it clear how to add other server routes that should not go through angular.
var http = require('http');
var express = require('express');
var app = express();
app
.use(express.static('public'))
.get('/home/login', function (req, res) {
console.log('Login request');
res.status(200).send('Login from server.');
})
.all('/*', function ( req, res ) {
console.log('All');
res
.status( 200 )
.set( { 'content-type': 'text/html; charset=utf-8' } )
.sendfile('public/index.html' );
})
.on( 'error', function( error ){
console.log( "Error: \n" + error.message );
console.log( error.stack );
});
http
.createServer( app ).listen( 8080 )
.on( 'error', function( error ){
console.log( "Error: \n" + error.message );
console.log( error.stack );
});
console.log('Serving app on port 8080');
index.html
is pretty simple.
<!doctype html>
<html>
<head>
<title>Angular HTML5 express test</title>
<script type="text/javascript" src="angular.js"></script>
<script type="text/javascript" src="angular-route.js"></script>
<script type="text/javascript" src="main.js">
</script>
</head>
<body ng-app="app">
<div ng-view></div>
</body>
</html>
main.html
just adds some content. Important is the link to /test
<div>
<h1>This is just a {{val}}</h1>
<button ng-click="clicked()">Click me!</button>
<span>You clicked {{counter}} times</span>
<a href="/test">test me!</a>
</div>
test.html
is actually irrelevant
<div>
<h4>What a beautiful day to test HTML5</h4>
</div>
main.js
is doing the core angular html5 work
angular.module('app', ['ngRoute'])
.config(function($locationProvider) {
$locationProvider
.html5Mode({
enabled: true, // set HTML5 mode
requireBase: false // I removed this to keep it simple, but you can set your own base url
});
})
.config(function($routeProvider) {
$routeProvider
.when('/test', {templateUrl: 'test.html', controller: function() {
console.log('On /test.');
}})
.when('/', {templateUrl: 'main.html', controller: 'MyTestCtrl'})
.otherwise('/');
})
.controller('MyTestCtrl', function ($scope) {
self = $scope;
self.val = 'TeSt';
self.counter = 0;
var self = self;
self.clicked = function() {
self.counter++;
};
});
Solution 2
instead of:
router.get( '/*', function( req, res ) {
express.static( indexPath )
})
do
router.get( '/:anyreq', function( req, res ) {
express.static( indexPath )
})
just keep it at end of routes file.
Related videos on Youtube
Noah
Updated on September 14, 2022Comments
-
Noah over 1 year
I know there are other answers to this but they seem to have caveats.
This one causes a redirect, which can be fatal for my front-end app which uses Mixpanel, and a double-load of Mixpanel will a Maximum Call Stack Size Exceeded error in the browser.
This one uses
sendFile
which I personally cannot get to work. Trying to diagnose that, I'm just advised to useexpress.static()
.Currently my code looks like this:
home.js - Some routes, the last one intended to be the front-end site.
var indexPath = path.resolve( __dirname, '../' + process.env.PUBLIC_FOLDER ) router.get( '/:user/:stream/:slug', function( req, res, next ) { if ( req.headers['user-agent'].indexOf( 'facebook' ) != -1 ) { // stuff to handle the Facebook crawler } else return next() }) router.get( '/*', function( req, res ) { express.static( indexPath ) })
server.js - configuring node/express
app = express(); app .use( morgan( 'dev' ) ) .use(bodyParser.urlencoded( { limit: '50mb', extended: true } ) ) .use( bodyParser.json( { limit: '50mb' } ) ) .use( '/api', require('./routes/usersRoute.js') ) .use( '/', require( './routes/home' ) ) .on( 'error', function( error ){ console.log( "Error: " + hostNames[i] + "\n" + error.message ) console.log( error.stack ) }) http .createServer( app ).listen( process.env.PORT ) .on( 'error', function( error ){ console.log( "Error: " + hostNames[i] + "\n" + error.message ) console.log( error.stack ) })
Some more info
The reason you can see I'm trying to use
express.static()
is because when I useres.sendfile()
I get a problem like this one where the console saysUnexpected token '<'
. Unfortunately the answer doesn't specify an exact fix and neither does the questioner who says they fixed the problem but don't share an answer.In my trial and error I have added some more to express, like this
.use( '/app/app.js', express.static( indexPath + '/app/app.js' ) ) .use( '/app/views', express.static( indexPath + '/app/views' ) ) .use( '/app/controllers', express.static( indexPath + '/app/views' ) ) .use( '/app/directives', express.static( indexPath + '/app/views' ) ) .use( '/app/vendor', express.static( indexPath + '/app/vendor' ) ) .use( '/js', express.static( indexPath + '/js' ) ) .use( '/css', express.static( indexPath + '/css' ) ) .use( '/fonts', express.static( indexPath + '/fonts' ) ) .use( '/images', express.static( indexPath + '/images' ) ) .use( '/api', require('./routes/usersRoute.js') ) .all( '/*', require( './routes/home' ) )
And in my
home.js
routes files added thisrouter.get( '/*', function ( req, res ) { res.status( 200 ).set( { 'content-type': 'text/html; charset=utf-8' } ) .sendfile( indexPath + '/index.html' ) })
And in the browser I can see all my files are loading, but with the
<
error above. I see this/*/
route is being called hundreds of times when I do a refresh so I think the.use( '...', ... )
configurations are being ignored.
Here is another example requested by Jonas below.
var indexPath = path.resolve( __dirname, process.env.PUBLIC_FOLDER ) mongoose.connect( process.env.MONGOLAB_URI ) app = express(); app .use( morgan( 'dev' ) ) .use(bodyParser.urlencoded( { limit: '50mb', extended: true } ) ) .use( bodyParser.json( { limit: '50mb' } ) ) .use( '/api', require('./routes/usersRoute.js') ) .use( '/', require( './routes/home.js' ) ) .use( express.static( indexPath ) ) .on( 'error', function( error ){ console.log( "Error: " + hostNames[i] + "\n" + error.message ) console.log( error.stack ) })
I have also done the same without the
.use( '/', require( './routes/home.js' ) )
line to try narrow down any problem, but it's the same result. The page will load if I have the#
in the URL, but the browser will remove the#
(so far so good). But if I press refresh, or put in the URL manually, sans-hashbang, it will give an error likeCannot GET /home/splash
, where/home/splash
is whatever path I'm going to. -
Noah over 8 yearsWhen I use
express.static
with HTML5 mode in Angular, I get the errorCannot GET [path to page]
after refreshing or manually entering a URL. If I add the#
back to the URL, I can view the page and the # will have been removed. -
Jonas over 8 years@Noah Can you create an example?
-
Noah over 8 yearsAre you telling me you know for sure that
.use(express.static( indexPath ))
supports HTML5? Because it doesn't seem to. It works until I hit refresh, then it saysCannot GET /home/splash
. If I put the#
back in the URL, it loads and removed the#
, but refreshing again gives the error again. -
Noah over 8 yearsI get
Cannot GET /home/login
when using a path andCannot GET /
when using a#
in the path. -
Noah over 8 yearsI am using
$http
on the front-end and declaring thetemplateUrl
like you explain. Are you usinghtml5Mode
? Can you confirm it works with Express? -
Noah over 8 yearsI've appended an example above.
-
Jonas over 8 yearsI made an example myself as well. Please check my update to the answer. I hope it clarifies something for you :)
-
Isaac Madwed over 8 yearsI'm using angular and express with that set up. In the static router you need to provide the relative path (ie
path.join(__dirname, "public")
) -
Noah over 8 yearsI think the issue was that I didn't put
.use(express.static('public'))
first, I always put it last. -
Admin over 7 yearsI found a bug in your code. <a href="/test/title">test me!</a> Modifying the router to handle the above link: .when('/test/title', ........ Cause an infinite loop. You can see it if you look at the console output.