Event aggregation with Backbone
This is the sixth post in my blog series on Backbone.js. If you haven’t had a chance yet, make sure to catch up on the first 5:
- Hey, check out my Backbone
- Aligning my Backbone
- Backbone Routers Are Configuration
- Grokking Backbone Views
- Async Template Loading with Backbone
In my second post I talked about how I employ the MVP design pattern to keep components of my JavaScript apps nicely decoupled. Interestingly, since that post, there have been some rumblings on the interwebs calling this pattern ‘MOVE’. I think you’ll see it popping up more and more as it gains momentum.
In my first post, I also mentioned that when you look at Backbone through an “MVC lense”, initially people often make a few incorrect assumptions on how the app should work. This is often based on their understanding of how a server-side MVC app functions. It certainly was in my case. In this post I’m going to cover how this approach differs from the common understanding of server-side MVC.
Getting Pub/Sub
One piece of knowledge hit me last fall as I was writing the Facebook authentication app (in Backbone/CoffeeScript) for the latest version of CentralAZ.com. Here it is: JavaScript apps written on the client are not stateless. If you’ve written lots of client apps in your career, then this probably isn’t a huge surprise to you, but this little tidbit of knowledge totally blew my mind. My focus had been on the server for so long, that I projected a lot of what I knew about ASP.NET MVC and Rails onto Backbone. This post by Derek Bailey really drove that point home for me. In a Backbone application, the model is a representation of the current application state.
mind === 'blown' > true
In Backbone, the preferred method of cross-communication between app components or modules is to use the Observer pattern, also commonly referred to as “pub/sub”. In Backbone, you get a very nice way implementing this pattern via extending Backbone.Events. This allows different components of an app to send messages or listen for messages being broadcast by an event aggrigator without having to know where those events are coming from.
How does that work?
To illustrate, we’ll assume each JS module lives in its own file and that the app is composed when the page loads via AMD (Asyncronous Module Definition) in Require.js. It’s not important to fully grok AMD to understand how the following code samples work together, the important thing to understand, though, is that each of these JS snippets represent separate JS files. They’re fully encapsulated and know nothing of each other, beyond the dependencies they request in the first parameter of the define()
function call.
// Define a module to return an event aggrigator object extended from Backbone.Events. // Let's call this events.js. define(['underscore', 'backbone'], function (_, Backbone) { return _.extend({}, Backbone.Events); });
Let’s pretend we have an app that allows a user to authenticate and view their own personal information. In this sample app, we’ll be listening for 2 different events. We want to listen for an event that signifies that a user intends to log out of their account. But we also want to know when the user has actually been logged out by the server. In Backbone apps, it’s considered good practice to namespace your events by object or model type. An event name can be any string value, but in order to keep things organized and easy to understand, we’ll use this convention:
events.trigger('object:property'); // or events.trigger('object:action');
OK, so now that we’ve got our event aggregation object, we’ll listen for the user:loggedOut
event in the initialize
method of our router. When a user clicks on a “logout” button, the user should be redirected to the home page once the server successfully logs them out. We’ll take a pessmistic approach to this event, meaning that we’ll wait to make sure “logout” actually happened on the server before we redirect.
// Defining a router module, called router.js define(['backbone', 'events', 'presenter'], function (Backbone, events, Presenter) { var router = Backbone.Router.extend({ routes: { '': 'index', 'myprofile': 'userProfile' }, initialize: function (options) { // Wiring up the presenter just like we did in a previous post... this.presenter = new Presenter(options); // _.bindAll is important here, because `this` will not be the current // instance of our router when it the events bound below get triggered. _.bindAll(this); // Here's the magic that binds a function to the `user:loggedOut` event events.on('user:loggedOut', this.onLogout); }, // Here's our event handler. It accepts a user model object, but we're not // going to use it here... onLogout: function (user) { // Call Backbone.Router.navigate here to tell our router to update the // URL and log browser history, sending the user to the home page. The // empty string in this call corresponds to the empty string in the // routes declaration above. this.navigate('', true); }, // Route handler definitions that get bound based on routes defined above index: function () { this.presenter.showIndex(); }, userProfile: function () { this.presenter.showUserProfile(); } }); return router; });
Additionally, we’ll have a view called authenticationView.js
that listens for the user:loggedOut
as well, that will fire when the user is successfully logged out by the server.
// Defining a authentication view module called authenticationView.js define(['backbone', 'events', 'templateManager'], function (Backbone, events, TemplateManager) { var view = Backbone.View.extend({ template: 'userAuthentication', className: 'user-authentication', initialize: function (options) { this.model = options.model; _.bindAll(this); // When the user logs out, we just want to re-render this view events.on('user:loggedOut', this.render); }, // Nothing extra special going on in our view's render method. Our template will // check the model's `isAuthenticated` flag to see if the user is currently // logged in. render: function () { var that = this; TemplateManager.get(this.template, function (tmp) { var html = tmp(that.model.toJSON()); that.$el.html(html); that.rendering(); }); return this; }, // In a previous post I covered having your views clean up after // themselves to avoid memory leaks. Here's a good example of this // in practice. In `onClose`, we'll unsubscribe from `user:loggedOut` onClose: function () { events.off('user:loggedOut'); } }); return view; });
Here’s what our corresponding Handlebars client-side template would look like. We’ll call it userAuthentication.html
.
{{#if isAuthenticated}} <a href="#">Howdy {{name}}!</a> {{else}} <a href="#">Log In</a> {{/if}}
We’ll also have a second view that has a “Log Out” button that will fire the user:loggingOut
event to get things kicked off.
// Defining our userProfile.js module. define(['backbone', 'events', 'templateManager'], function (Backbone, events, TemplateManager) { var view = Backbone.View.extend({ template: 'userProfile', className: 'user-profile', events: { 'click #log-out', 'logoutClicked' }, initialize: function (options) { this.model = options.model; _.bindAll(this); }, render: function () { var that = this; TemplateManager.get(this.template, function (tmp) { var html = tmp(that.model.toJSON()); that.$el.html(html); that.rendering(); }); return this; }, logoutClicked: function () { // Fire the `user:loggingOut` event to kick things off. Make sure to pass // along a reference of the current user. events.trigger('user:loggingOut', this.model); return false; } }); return view; });
You might be asking yourself, “That’s great, but how does the user actually get logged out?”
Great question. We’ll just wire up another listener on the presenter to tell the model to log itself out.
// For posterity, here's what our presenter.js would look like. define(['events', 'userModel' 'indexView', 'userProfileView'], function (events, UserModel, IndexView, UserProfileView) { var Presenter = function (options) { this.model = options.model || new UserModel(); _.bindAll(this); // We'll set up a handler here to listen for `user:loggingOut` events.on('user:loggingOut', this.onLogout); }; Presenter.prototype.showView = function (view) { if (this.currentView) { this.currentView.close(); } this.currentView = view; $('#container').append(this.currentView.render().$el); }; Presenter.prototype.showIndex = function () { var view = new IndexView({ model: this.model }); this.showView(view); }; Presenter.prototype.showUserProfile = function () { var view = new UserProfileView({ model: this.model }); this.showView(view); }; // Here's our handler for `user:loggingOut`. We'll have the user model call logout // on itself. Presenter.prototype.onLogout = function (user) { var promise = user.logout(); // `user.logout` returns a jQuery deferred, so we can bind a handler to trigger // the `user:loggedOut` event when the ajax call completes successfully. promise.done(function () { events.trigger('user:loggedOut', user); }); }; return Presenter; });
Finally, to complete the story, we’ll wire up our user model so it knows how to to log itself out.
// Defining a module for our user model called user.js define(['backbone', 'events'], function (Backbone, events) { var model = Backbone.Model.extend({ initialize: function (options) { _.bindAll(this); }, logout: function () { var that = this, promise = $.trafficCop({ type: 'POST', url: '/user/logout', data: this.toJSON() }); // Bind a callback to our promise to set the `isAuthenticated` flag // to `false` when logout completes successfully. promise.done(function () { that.set({ isAuthenticated: false }, { silent: true }); }); return promise; } }); return model; });
But why not just use jQuery’s built in event pool?
It’s true that jQuery has its own version of Backbone.Events that looks the same, and is implemented, in much the same way. The Backbone team, I believe, decided to roll their own version for two good reasons:
- If I understand correctly, when Backbone was first being written, jQuery’s event pooling employed a DOM-based eventing system (e.g. -
document.addEventListener
, etc). Since then, jQuery has moved to an object-based eventing system similar to Bacbkone’s. - In order to keep the dependency tree light, Backbone’s only dependency is Underscore. There’s not a hard dependency on jQuery (nor should there be). Using jQuery’s eventing system would require a hard dependency on jQuery.
What’s with _.bindAll
being called everywhere?
A lot of people new to Backbone may not be too cozy with Underscore (yet). Basically, calling _.bindAll(this)
will ensure that all methods of the object passed in will have the correct context of this
bound to its calling context. Doing this basically guarantees that this
will represent the current object in scope, rather than window
, a DOM element, or some other less predictable thing.
That’s what I’ve got for now. Does your mind hurt yet? When you recover, do you think you might use this approach in your apps? Let me know if in the comments below.
Backbone Routers Are Configuration
This is the third post in my series on Backbone.js Make sure to catch up on the first two if you haven’t already.
In my last post I covered how using the Model View Presenter design pattern will help you write cleaner apps without cluttering your Routers with unnecessary application or presentation logic. In this post, I’m going to get into the guts of the Backbone Router object, demonstrate how I’ve used it, and hopefully dispel some misunderstandings about its use.
Routers are really just configuration files
When we look at other MVC-like frameworks (especially on the server side), they all use a router to control how incoming URLs get mapped to the appropriate controller action method. Backbone.js is no different in that respect.
Let’s consider a couple examples from Backbone’s server-side cousins. In ASP.NET MVC, you set up your route tables in Global.asax.cs
:
public class FooApplication : MvcApplication { protected void Application_Start() { RegisterRoutes(RouteTable.Routes); } public static void RegisterRoutes(RouteTable routes) { // In ASP.NET, you have to define routes explicitly via routing table // definitons. These work similarly to how Backbone does things. routes.MapRoute( "Foos", "foos/{action}/{id}", new { controller = "Foo", action = "Index", id = UrlParameter.Optional } ); routes.MapRoute( "Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } }
Or in Rails, your routes.rb
file probably looks something like this:
FooApp::Application.routes.draw do # In Rails, however, you can define a `resource` via a model symbol name and # the framework just knows how to map RESTful URLs to it. resources :foos # You can also define individual URLs and map them manually. match '/about' => 'pages#about' root :to => 'pages#home' end
When we consider these two popular frameworks and how they treat their respective routing systems, we can draw the same conclusion about both of them. Routers are configuration. Yes, they’re code files and not XML, Yaml, JSON or some other structured data file, but actual imperative code. However, any sane developer would never put any sort of business logic in these files, right? Right?
So if the established use pattern for routers in an MV* framework is to treat them as configuration files, then why treat Backbone Routers in any other way?
Not to sound all soap-boxy, but here’s how I’ve been setting up my Routers in Backbone (with comments to hopefully help explain the important bits).
// Here's our Router "class" definition. There are a few things we need to wire up // before it's ready for action... var Router = Backbone.Router.extend({ // `initialize` is a standard part of every Backbone construct. It behaves // kind of like a constructor that accepts an object called `options`. initialize: function (options) { // Since I often use a presenter, I will instantiate it here and delegate // all application logic to it. Note that we're passing the options object // strait through to the Presenter. this.presenter = new Presenter(options); }, // This is our route table. In this JSON object, we can define any URL we // want to handle on the left side of the ':', and on the right side, we // list the name of the method that will handle that URL. routes: { '': 'index', // Empty string will represent "home" 'about': 'about', 'foos': 'listFoos', 'foos/:id': 'showFoo', // Note the 'id' URL param used here '*options': 'notFound' // `*options` is a catchall route in Backbone }, // Here's our handler for our home page. So if our website's address is // `http://foo.com`, a route with an empty string would catch the root // URL of the site. index: function () { this.presenter.showHomePage(); }, // Handler for the `http://foos.com/foos` URL listFoos: function () { this.presenter.showFooList(); }, // Here's the handler for `http://foos.com/foos/:id`. Notice that the URL parameter // `id` is mapped below as a function argument. showFoo: function (id) { this.presenter.showFoo(id); }, // The `*options` catchall route is a well known value in Backbone's Routing // internals that represents any route that's not listed before it. It should to // be defined last if desired. We'll just have the presenter render a 404-style // error view. notFound: function () { this.presenter.showPageNotFound(); } }); // To actually use our router, we'll just "new it up" and pass in any options necessary. // Let's pretend we already have a model populated with data that we're passing to it. var router = new Router({ model: foos }); // Now that we have a router object loaded in memory, we can start recording URL // history. Backbone.history worries about watching the URL and calling the appropriate // functions listed in the router's `routes` object. Here we're going to use HTML5 // PushState, so that we can generate cleaner looking URLs rather than hash-based ones. Backbone.history.start({ pushState: true });
Hopefully that helps demystify Backbone’s routing system for those unfamiliar with how it works. When you really break it down to its most important bits, the Backbone.Router construct really is just fancy looking configuration. Think this’ll help you write cleaner code? Let me know in the comments.
Aligning My Backbone
This is part 2 in my blog series on Backbone.js. Make sure to check out the first post if you haven’t read it already.
In my last post I covered the basics on each part of Backbone and provided a very basic intro. Just to reiterate, Backbone.js is a MV* framework for writing complex single-page JavaScript apps. It’s just plain ol’ JavaScript and is very un-opinionated. These design choices make Backbone very flexible and incredibly powerful. My focus in this post will be why adding a bit of your own flavor, patterns and opinions can be highly productive.
Model View Presenter
While building lots of Backbone apps over the past 9 months or so, I’ve gotten cozy with the MVP design pattern. In MVP, the notion of a Controller is replaced by that of a Presenter. A Presenter behaves like a super-Controller. It not only concerns itself with acting as a proxy for the Model, it actually has some say over injecting the Views into the DOM and managing their lifecycle.
But… But… But… Backbone doesn’t have a Presenter! How could you possibly use MVP in a Backbone app?
It took me a little while to wrap my head around this. So don’t feel bad if you find yourself asking this very same question. I’ve asked it, and I’m sure many others have too. You can create your own Presenter object. Not every piece of a Backbone app has to be a Backbone construct. Let that sink in. Once you understand that you’re just writing JavaScript, it’s incredibly liberating.
OK fine. Enough talking. Show me some code!
At first glance you’ll note that most of my code looks like the standard stuff you’ll see in a Backbone app. The main differences will be seen in the Router and the Presenter itself.
Here’s what the router would look like…
var FooRouter = Backbone.Router.extend({ routes: { 'foos': 'listFoos', 'foos/:id': 'getFoo' }, initialize: function (options) { // Pass options through to the Presenter this.presenter = new FooPresenter(options); _.bindAll(this); }, listFoos: function () { this.presenter.listFoos(); }, getFoo: function (id) { this.presenter.getFoo(id); } });
No too shabby, eh? We’re just passing everything through from the Router to the Presenter. Keeps the router nice and lean. It removes unnecessary logic and lets the Router focus on… um… routing!
And here’s how the Presenter might look…
var FooPresenter = function (options) { // Let's pretend that we've already got a list of Foos // hydrated from the server. this.foos = options.model; _.bindAll(this); }; FooPresenter.prototype.showView = function (view) { if (this.currentView) { // I often create a 'close' method off of Backbone.View // to help Views clean up after themselves and to // prevent memory leaks. I'll get into this in a future // post on Views. this.currentView.close(); } this.currentView = view; $('#some-container').html(this.currentView.render().$el); }; FooPresenter.prototype.listFoos = function () { var view = new FooListView({ model: this.foos }); this.showView(view); }; FooPresenter.prototype.getFoo = function (id) { var foo = this.foos.get(id), view = new FooView({ model: foo }); this.showView(view); };
That’s pretty much all there is to it. If you’re familiar with how “classy” objects get created in JavaScript and how the prototype chain works, you’re in good shape. And just like with MVC Controllers, you want to keep your Presenter methods nice and lean as well. Nothing super complicated going on here.
Remember, adding patterns you’re already familiar with will help you become a better JS developer and will help you design little programs that you can compose into large, testable systems.
In my next post, I’ll cover how to manage messaging between the various components of your app to keep things nicely decoupled.
Hey, check out my Backbone
This is the first post in what will become a series of posts covering Backbone.js. I’ve been developing Backbone apps pretty heavily lately and have learned a lot. This series will serve to document what I’ve learned for personal purposes, and hopefully will be helpful for developers looking to learn more about it.
What’s a Backbone.js?
Backbone.js is a JavaScript framework. Lots of people like to use it to build larger scale applications that run in a web browser, completely decoupled from their server framework of choice. It’s an incredibly powerful tool to have at your disposal. There’s so much information and buzz out there about this framework, though, that I think it may be best to start off describing what Backbone isn’t.
Huh?
You’ll often hear Backbone referred to as a client side MVC framework, like Rails or ASP.NET MVC. I disagree with this comparison. While its useful to provide context for what Backbone can do, it leads down a very confusing path where many incorrect assumptions get made.
These bad assumptions often get made because of an unfortunate naming choice early on in the framework’s development. In previous versions, the Backbone Router was actually called “Controller”. Thankfully, right around the 0.5.3 release, Jeremy Ashkenas and team decided to rename “Controller” to reflect its actual purpose: a Router.
Confusing naming conventions aside, I like to call Backbone an MV* framework, where the * is basically a wild card (you could go with MVC, MVP or MVVM). Backbone has Models and Views. It does not, however, come with a baked in Controller (in its most current form). Because of the unfortunate naming mishap in earlier versions, many demos you’ll run into out on the web will treat the Router as if it were a Controller. While this works from a technical standpoint, and is often the simplest implementation, I’ve found that it tends to lead toward a more limited understanding of the framework. I’ll get into more detail on that later on in this series.
I’m not going to go super in depth about each individual part of Backbone just yet, but here’s the 10k foot overview:
Models
Like in most MV* implementations, in Backbone, models represent your data. They also have a lot of nice utility methods to handle data transport. You can tell a model where it lives on the server by passing it a RESTful URL root. And should you ever call ‘save()’ on that model, it will know how to persist itself. And, of course, you’ll want to place much of your domain-specific business logic in these model objects.
Collections
Backbone collections are basically lists of models. The really nice thing about the way they’re implemented. Thanks to Underscore, it’s really easy to query, slice and dice your data on the client.
Underscore.js is a dependency of Backbone and is super useful in its own right. Underscore provides lots of nice utility functions that Backbone hooks into. Underscore is to plain ol’ JS as jQuery is to the DOM.
Views
Views are the representation of your UI reflected as code. You get a lot of flexibility in how you choose to implement them, though. They can already exist on the page (rendered by the server), or you can populate templates (HTML fragments) with data from your models and have the view render them on the page.
Views can listen for DOM events via jQuery or Zepto (whichever you happen to be using). This often makes Views feel a bit like Controllers in that they are often the entry point for user interaction in your app.
Routers
As I mentioned earlier, a lot of people make the mistake of thinking of the Backbone Router as a Controller, simply because it handles incoming URLs in a similar fashion that server-side MVC developers are often familiar with. In an upcoming post, I’ll be diving into the pattern I use to avoid falling into this trap.
Events
Internally, Backbone uses an object based event binding system to enable a really elegant publish/subscribe implementation. You can use this feature to listen for events fired by models and collections. You can also extend this functionality to create your own event aggregator for your app, allowing different constructs or modules to talk to each other without unnecessary dependencies.
History
Backbone’s implementation of browser history is where a lot of the magic happens. It watches the URL hash, or listens to PushState (if your app uses it, and the browser supports it), and will send the URL changes to your router to handle. This is really nice in that you can add bookmarkability into your single page JavaScript applications for free.
Wrapping up
Individually, these pieces aren’t super special, but as a package, they become really powerful. It’s also important to note that Backbone plays really nicely with other JS libraries, so it’s very easy to mix and match things to create your own stack.
In my next post, I’ll be digging into the MVP (Model View Presenter) pattern that I use to build apps, and why adding a bit of your own opinions into Backbone can be really important and highly beneficial.
Musings on asynchronous programming in JavaScript
I had a brief discussion today with my buddy Nick and something he said struck me. And it may come across that I’m trying to pick on him in my post. But our little chat really got me thinking, as they often do (which makes Nick such an awesome guy to code with). So I thought I’d share…
I’ll try to paraphrase Nick’s statement as best I can. Here’s the gist of what he said regarding writing large-scale, asynchronous applications in JavaScript (Backbone.js in this case):
This technique of coding adds a completely different level of complexity. It’s not going to catch on until it gets easier for developers to work with.
I agree with Nick’s first thought. Asynchronous programming is a whole new ball game. It’ll melt your brain in ways you never conceived your brain of ever being melted before.
… However …
Async programming is still fairly new. It wasn’t born yesterday, but it’s new in the sense that it’s just now getting it’s time in the spotlight thanks to projects like Node.js, jQuery, and Backbone.js. It’s new to a lot of us who have been stuck in the synchronous world for the past few decades.
I disagree that async development (particularly using JavaScript in large scale applications) will have to get “easier” to be widely successful. I think it’s going to require a different mindset of people who choose to work in that space.
Asynchronous code is definitely not “the path of least resistance”. It’s a different animal altogether. It’s a complete paradigm shift, not unlike what the development community went though when hackers writing imperative code had to really sit down and grok Object Oriented Programming. I would also say that its probably not for everybody, and certainly not suitable for every project.
I think for developers to be successful in writing asynchronous code, they need to stretch themselves a bit to wrap their minds around it and invest the time necessary to do it right. I’m sure there will be more and more libraries that come out that attempt to reduce the “callback hell” that comes with the territory, but the fundamental building blocks used will remain the same (closures via callbacks, promises, etc).
It’s awesome to see it maturing at such a staggering rate. This stuff makes it a fantastic time to be a developer.
Steve Jobs - by Walter Isaacson
I’m normally not one to write book reviews. If it’s not directly related to design or code, I usually dont bother posting about it. I suppose I can make an exception just this once.
I just finished reading the biography on the life and career of Steve Jobs by Walter Isaacson. It was brilliant. All I can really say is “Wow”. I’ve got a newfound appreciation for who he was as a person, his genius and his accomplishments.
I saw a lot about his personality to disagree with, but I found that I resonated with his tastes and character far more than I expected, and I picked up a bunch of valuable takeaways from his experience. If you build or create anything in your career, whatever that may be, and you actually care about what you’re creating, you really ought to read this book.
More than just a house
This past Saturday night my wife, Rebecca, and I went to 1Mission’s 2012 Gala and it was awesome. The guys that run that charity never cease to amaze me with the innovative things they do. This weekend, I got my bell rung pretty good.
I’m no stranger to their cause. They build houses for the impoverished people living in the barrios of Puerto Penasco, Mexico. But I think that description of their mission is highly misleading and undermines what it is that they actually do. 1Mission is empowering these people to fix their own circumstances. 1Mission doesn’t do traditional aid-type charity work. They partner with the locals by educating them, giving them a sense of ownership over their situation, thereby giving them a means to pull themselves out of it.
The video below is a great illustration of what they actually do…
1Mission has a unique take on the problem of poverty, and behind that perspective, they’ve created a very effective process. It’s working wonders in their Penasco community. So much so, that other organizations around the globe are asking to partner with 1Mission so they can learn the process.
It’s because of Jason Law, the founder of 1Mission, and his team’s vision that I even began my work on Grassroots. It’s an honor for me to be able to name some of these guys as friends and I consider myself to be super lucky to be involved, even in a small way, in what they’re doing. I’m blessed to be able to partner with guys like this that “get it” when it comes to building Open Source Software that can have a profound impact on the people around them.
OK, enough mushy stuff. I’ll leave you with one last video from the Gala this weekend. Hope you enjoy! Back to hacking!
Source: 1mission.org