Guiding The Geek In You
Building an AngularJS application using partial views and supported by a Node.js / ExpressJS back-end is doable with a little effort.
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:
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 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')); });
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!