Angular routing in HTML5mode with Node.js

10,326

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.

Share:
10,326

Related videos on Youtube

Noah
Author by

Noah

Updated on September 14, 2022

Comments

  • Noah
    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 use express.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 use res.sendfile() I get a problem like this one where the console says Unexpected 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 this

    router.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 like Cannot GET /home/splash, where /home/splash is whatever path I'm going to.

  • Noah
    Noah over 8 years
    When I use express.static with HTML5 mode in Angular, I get the error Cannot 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
    Jonas over 8 years
    @Noah Can you create an example?
  • Noah
    Noah over 8 years
    Are 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 says Cannot GET /home/splash. If I put the # back in the URL, it loads and removed the #, but refreshing again gives the error again.
  • Noah
    Noah over 8 years
    I get Cannot GET /home/login when using a path and Cannot GET / when using a # in the path.
  • Noah
    Noah over 8 years
    I am using $http on the front-end and declaring the templateUrl like you explain. Are you using html5Mode? Can you confirm it works with Express?
  • Noah
    Noah over 8 years
    I've appended an example above.
  • Jonas
    Jonas over 8 years
    I made an example myself as well. Please check my update to the answer. I hope it clarifies something for you :)
  • Isaac Madwed
    Isaac Madwed over 8 years
    I'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
    Noah over 8 years
    I think the issue was that I didn't put .use(express.static('public')) first, I always put it last.
  • Admin
    Admin over 7 years
    I 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.