using AngularJS html5mode with nodeJS and Express

I'm using a nodeJS server with Express to serve my AngularJS application. This all works fine when I'm using angularJS default routes (hashbangs), but now I'm trying to activate html5 mode.

I'm activating html5mode like this:

$locationProvider.html5Mode(true).hashPrefix('!');

And this is what my nodeJS app.js file looks like:

var path     = require('path'),
    express  = require('express'),
    app      = express(),
    routes   = require(path.join(__dirname, 'routes'));

app.configure(function() {
    app.use(express.logger('dev'));
    app.use(express.compress());
    app.use(express.methodOverride());
    app.use(express.bodyParser());
    app.use(app.router);
    app.all("/*", function(req, res, next) {
        res.sendfile("index.html", { root: __dirname + "/../app" });
    });
    app.use(express.errorHandler({
        dumpExceptions: true, 
        showStack: true
    }));
});

However, this now serves all requests as my index.html file, and so I get the following error from requireJS:

Uncaught SyntaxError: Unexpected token < 

I tried adding the following to my nodeJS app.js so it would serve my resources correctly:

app.use("/js", express.static(__dirname + "/../app/js"));
app.use("/img", express.static(__dirname + "/../app/img"));
app.use("/css", express.static(__dirname + "/../app/css"));
app.use("/partials", express.static(__dirname + "/../app/partials"));

but still no luck.

I also tried replacing the app.all statement with:

app.use(function(req, res) {
  // Use res.sendfile, as it streams instead of reading the file into memory.
  res.sendfile(__dirname + '/../app/index.html');
});

but that didn't work either. What can I do to get angularJS html5mode working with nodeJS and Express? Thanks.

Answers:

Answer

Your initial fix (declaring static middleware handlers for specific prefixes) should work just fine, but you need to make sure they are declared before any other routes (and app.router, although you don't need to explicitly use it):

// these need to go first:
app.use("/js", express.static(__dirname + "/../app/js"));
app.use("/img", express.static(__dirname + "/../app/img"));
app.use("/css", express.static(__dirname + "/../app/css"));
app.use("/partials", express.static(__dirname + "/../app/partials"));

// any other routes:
app.all("/*", ...);

Also, you need to make sure that the prefixed static handlers are actually declared okay (correct path), otherwise they won't be able to find any requested files and the requests will pass down the middleware chain and ultimately be handled by the catch-all handler (should be easy enough to test by commenting out the catch-all handler and see if any JS/CSS/... requests work okay).

Answer

Configure express 4 server like:

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

app.get('/*', function(req, res){
    res.sendFile(__dirname + '/public/index.html');
});

and angular like:

app.config(function($stateProvider, $urlRouterProvider, $locationProvider){
    $stateProvider
        .state('home', {
            url: '/',
            templateUrl: 'templates/main.html'
        })
        .state('register', {
            url: '/register',
            templateUrl: 'templates/register.html'
        });

    $urlRouterProvider.otherwise("/");
    $locationProvider.html5Mode({
        enabled: true,
        requireBase: false
    });
});
Answer

I'm working on a web application using Angularjs and Requirejs on the client, with Nodejs on the server.

Here is some sample code to show you how I set it up.

Note this example is showing a hash url but you can easily change that by modifying the middleware function and angular configuration

Middleware function

isXHR: function (req, res, next) {
    if (req.xhr || req.get("angular-request") === "ajaxRequest") {
        next();
    } else {
        var url = req.url;
        var urls = url.split("/");
        var last = _.last(urls);
        urls = _.without(urls, last);
        url = urls.join("/") + "#/" + last //remove the hash if you want to make it html5mode;

        res.redirect(url);
    }
}

Server route configuration

//I'm using express-namespace to group my routes
app.namespace("/requirements", function(){
   //Shared local variable used across the application
   var info = {
        app: {
            title: "Requirements",
            module: "app/requirements" // where the angular application stored
        }
    }
    //this is the main url that will user request
    Route.get("/", function (req, res) {
        res.cookie("profileRegisterationSteps", 0);
        res.render("app/requirements/index", info);
    });
   //this is the url angular js will request
    Route.get("type", filters.isXHR, function (req, res) {
        res.render("app/requirements/profile/type", info);
    });
 })

Client route configuration

require(['App', 'underscore', 'ngAmd'/*angular amd*/, 'autoload'/*No Sense*/, 'appLoader' /*i used to load my scripts file for the route user requested (not all scripts files only who requested) before template laoded*/, 'appRoute'/*this is a list of routes*/], function (app, _, amd, autoload, loader, routes) {

app.config(function ($routeProvider, $locationProvider, $httpProvider) {
    //remove a clearn URL
    $locationProvider.html5Mode(false);

    //ku dar header kan si uu server ka u ogaado Request in yahay Ajax
    $httpProvider.defaults.headers.common['angular-request'] = "ajaxRequest";

    //Route Config
    var Route = $routeProvider;
    //get all routes objects that defined in routes module
    _.each(routes, function (route) {
        // extend the routes module objects and use its properties
        Route.when(route.url, _.extend(route, {
            //call returning function in loader module and write the promise
            resolve: _.extend(loader(route))
        }));
    });
    Route.otherwise({
        redirectTo: "/"
    });

});

  //start the application
  amd.bootstrap(app);
 });

Apploader file

require.config({
    paths: {
        //pages
       type: "Apps/requirements/pages/type"
   }
});
 define(['App'], function(app) {
    return function (options) {
      return {
        loader: function ($q, $rootScope) {
            var defer = $q.defer();
            var module = options.name// the name of the route (this name corresponds to the requirejs module name above;

            if (!!(module)) {
                if (require.defined(module)) {
                    defer.resolve();
                } else {
                    require([module], function () {
                        $rootScope.safeApply(function () {
                            defer.resolve();
                        })
                    });
                }
            } else {
                defer.resolve();
            }

            return defer.promise;
        }
      }
    }
 });

Routes file

define(function(){
  return {
     {
        name        : "type",
        url         : "/",
        templateUrl : "/requirements/type",
        view        : 'services'
    }

 }

})

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.