Compass In Hand

Guiding The Geek In You

Building an AngularJS app using Node.js and ExpressJS

Building an AngularJS application using partial views and supported by a Node.js / ExpressJS back-end is doable with a little effort.

Configuring AngularJS

Two methods exist for creating modules in AngularJS. I prefer the setter method. Note below we are injecting dependencies on our external controller and ngRoute. Both of these items source from external JS files, so don’t forget to attach them within your index.html.

angular.module('myApp', [
	'ngRoute',
	'myApp.controllers'
]).

Pro Tip: Ensure the angular.js version matches angular-route.js! Download them both at the same time from code.angularjs.org.

$locationProvider allows access to .html5mode(), a piece of Angular magic that ensures URLs look like regular URLs in modern browsers, and hash-bangs in older ones. Neat trick! Keep these two caveats in mind when configuring a Node.js / ExpressJS installation:

  • To support HTML5 mode the server must deliver index.html for all apps, and must support URL rewriting on the server-side.
  • Avoid using relative links when using html5mode (serving from root is fine), otherwise Angular chokes.
config(function ($routeProvider, $locationProvider) {
  $routeProvider.
    when('/', {
      templateUrl: 'partials/home',
      controller: 'HomeController'
    }).
    when('/pizza', {
      template: 'I Love Pizza!'
    }).
    otherwise({
      redirectTo: '/'
    });

  $locationProvider.html5Mode(true);
});

Standard configuration from the Angular perspective thus far. The controller is also super basic – just stubbed out for later use:

angular.module('myApp.controllers', []).
  controller('HomeController', function ($scope) {

  });

Configuring Node.js & ExpressJS

Configuring Node.js correctly (via the ExpressJS framework) is tricky. However, thanks to btford’s excellent seed project (available on github) I refined this:

var express = require('express'),
	routes  = require('./routes'),
	http    = require('http'),
	path    = require('path');

var app = module.exports = express();

app.engine('html', require('ejs').renderFile);

app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'html');
app.use(express.logger('dev'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(app.router);

if (app.get('env') === 'development') {
  app.use(express.errorHandler());
}

Pro Tip: Make sure this line:

app.use(express.static(path.join(__dirname, 'public')));

comes before this line:

app.use(app.router);

Order matters! We need Node.js to serve the static page request before passing the request to a route handler. This way AngularJS $routeProvider works as it should.

app.get('/', routes.index);
app.get('/partials/:name', routes.partials);

app.get('*', routes.index);

The code above is the connective tissue between AngularJS and Node.js. First, note the references to routes.index. They point to index.js, the JS file containing the specific routing information about the three potential GETS. The first and third lines will route all traffic to the index page, and line two routes requests to the partials directory (rendering any partials found within). Line three in particular is important, as this catch-all is vital for enabling html5mode in our AngularJS configuration.

Index.js:

exports.index = function(req, res){
  res.render('index');
};

exports.partials = function (req, res) {
  var name = req.params.name;
  res.render('partials/' + name);
};

And finally, to kick off our Node.js server:

http.createServer(app).listen(app.get('port'), function () {
  console.log('Server listening on port ' + app.get('port'));
});

The entire Node.js configuration file:

/*********************
 * Module dependencies
 *********************/

var express = require('express'),
	routes  = require('./routes'),
	http    = require('http'),
	path    = require('path');

var app = module.exports = express();

app.engine('html', require('ejs').renderFile);

/***************
 * Configuration
 ***************/

// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'html');
app.use(express.logger('dev'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(app.router);

// development only
if (app.get('env') === 'development') {
  app.use(express.errorHandler());
}

/********
 * Routes
 ********/

// serve index and view partials
app.get('/', routes.index);
app.get('/partials/:name', routes.partials);

// redirect all others to the index (HTML5 history)
app.get('*', routes.index);

/**************
 * Start Server
 **************/

http.createServer(app).listen(app.get('port'), function () {
  console.log('Server listening on port ' + app.get('port'));
});

Pure HTML, Thank You

Awesome, an environment which supports AngularJS, runs Node.js / ExpressJS! All that’s missing now is the View.

If you are like me you prefer your HTML as pure as the driven snow. By default Express does not make this possible, as it requires the use of Jade, an HTML Template technology. Fortunately, Embedded JavaScript (ejs) is an easy replacement for Jade, affording us the luxury of a powerful framework and clean markup. Download and install ejs as you would any other node plug-in:

npm install ejs

That’s all folks. Comments, questions? I would love to hear from you. Don’t be shy, and…

Thanks for reading!


Leave a Reply