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!
Happy New Year! It has been a few weeks since my last post. My sabbatical was refreshing and productive. I finished my first major AngularJS project, which I am excited to start writing about, and of course took some much-needed down time. I hope your end of year festivities were equally pleasant.
This year I have a diverse topic list in store for CompassInHand that I hope will be as interesting to you as it is educational to me. Technology is amazing. There is no end to what can be learned. My hope is that I can give back some of what others are teaching me.
As mentioned, I released my first application written purely in the “Angular way”: Our XBOX 360 Library (OX360L)! If you are interested in checking out the code, please visit the project git repo.
Loyal readers will note: prior to my holiday sabbatical my posting interval was about once per week. Unfortunately I rarely had enough time to do the research, understand the technology, and write a quality post about it within the single week. Therefore my new posting interval will be about twice per month. The additional week will be a tremendous help and should make for tighter posts with more code examples.
While no release date exists yet, this year you can expect CompassInHand to undergo a big face lift. With the help of my good friend (and new father!) Trian Koutoufaris a new WordPress theme is slowly taking shape – including an actual site logo! Stay tuned.
Finally, I plan to begin supporting RSS feeds. This is not a big technical challenge, just an administrative oversight I never considered before. Whether this feature is useful will be up to you guys – please, comment and let me know!
Lots of things!
Over the next several posts I will be discussing some of the many (many, many) lessons I learned while building OX360L. Some of these lesson topics may include:
Looking past Angular / OXB360L, other post topics coming in the next several months will include:
Finally, I have another project getting ready to spin up. Look for hints and mentions as meaningful progress is made. Hopefully it can be useful for all of you who go to lunch with coworkers.
Thanks for reading!