.
Also, while I’ll have a lot of future articles on web development, rather than reading 20 billion novels, if you’re just looking to get immersed in as much web dev as possible to get a 30,000ft view of where things stand, this is a better post to read even if dated. I’m still learning the web stack, so I’ll update this post with any inaccuracies pointed out. Just comment or send me an email/tweet.
What Is Backbone?
Backbone is basically an MVC framework for writing JavaScript web applications. If you’ve ever messed with Cairngorm, PureMVC, Robotlegs, Swiz, or Parsley, you’ll instantly see a lot you recognize. Like a lot of JavaScript MVC frameworks, it is a collection of JavaScript libraries.
Also, it like others has the intentional ability to easily integrate with other toolsets and libraries. Examples include UI component frameworks like jQuery or Sencha Touch, HTML template engines like Handlebars, and it assumes you’ll either hand code your CSS or use something like SASS/LESS if you need to make your CSS more manageable on larger projects.
While Cairngorm and PureMVC were self contained, Robotlegs depended on SwiftSuspenders for the Dependency Injection functionality… however it is known that you just used Robotlegs without really caring, nor knowing buried in the robotlegs.swc file, there was a Swift Suspenders dependency.
Dependency management is tons harder in JavaScript, but I’ll briefly cover that plus more on Underscore in a bit. The same holds true for Backbone. It utilizes a library called Underscore to basically fix JavaScript to a certain standard of usability, add many OOP and Array features functional server-side devs love, and to abstract away the browser incompatibilities for the 1st and 2nd.
It also utilizes Douglas Crockford’s json.js, the man behind the book “JavaScript: The Good Parts” and a promoter of using closure based OOP vs. Object.prototype. His json library does the same, allowing the JSON format to work seamlessly in JavaScript regardless of browser; for older browsers it parses, for newer ones it uses the built in parser. You should be using json2 since it doesn’t mess with the prototype (omg, purism). You’ll notice, too, just about every article on JavaScript nowadays assumes this part is “handled”; you’ll see a magic “JSON” in the code. It’s implied it comes form this library, is native, or both. In a development environment, you just include all the libraries mentioned at the bottom if your index.html body tag (so all the GUI and CSS dependencies load first) in script tags.
Why Backbone?
If you’re a Flex Developer, you already know why: it’s an MVC framework. Sold. Feel free to skip this section or just skim 4 & 5.
I was thinking of deleting this section, but then read Smashing Magazine’s Journey Through The JavaScript MVC Jungle, and decided more details were needed for those who are questioning why they even need an MVC framework.
For those that don’t, MVC frameworks help separate out your visual, data, application, and business logic in distinct areas in your code. This allows you to have multiple developers work on the code, the code to more easily scale for larger applications, and to make it more testable. Putting logic in your DOM, aka your JavaScript mashed into your HTML and CSS on a “page”, makes things unmaintable, hard to find bugs, and unreadable. …for larger applications. Just like Flash, there is fine line that’s blurred as all get out. Whether you use an MVC framework or not depends on:
- your business
- your team
- size of your application
- multi-device
- server-side vs. client side
- website vs. app
Your Business
For those who have non-negotiable deadlines, any OOP, design patterns, and frameworks usually get in the way. Paying off technical debt never happens much, so spit and duct tape applications are fine. On the other hand if your business is creating code that will have to live for years and/or is a product for consumers or other businesses, using OOP, design patterns, and a framework is a good idea. Usually. Sometimes you need to just launch and get funding. Also, there is always the risk that a “Prototype” suddenly becomes “The Product”. Starting out with even a few parts in a framework will prevent a lot of pain down the line.
Your Team
If you’re a 1 woman show, and management has done the estimates to show that all design, development, testing, and bug fixing can be done just by you, great; architect as you see fit.
However, if you’re on a team environment because your project is of the size that can in fact utilize multiple designers and developers to create value in tandem, an MVC framework can allow your team to work with each other, in different files, being productive using modern source control systems. Additionally, Backbone like many frameworks has enough industry traction that documentation & answers to common questions/problems are all online. This makes hiring & training both new & existing team resources much cheaper and faster. The code has SOME semblance of structure, even if the original developer(s) didn’t follow Backbone conventions to a T.
Application Size/Scope
If you’re application is just a few screens/pages/widgets, use jQuery, shove you’re services and data into a dozen files and call it a day. You could still even write it where your service and application logic layer was testable to get unit tests, all without the need of a framework. I’ve seen a lot of Flash Developers take to this route just fine over the past year for both desktop and mobile. Some even threw everything on the sever and just did HTML/CSS on the client.
If, however, you’re creating a large application with many screens, a lot of client side logic/data caching, and maybe even iterating on a design, then separating things out cleanly via Backbone, or some other framework, is the way to go. Again, this assumes you’re not using the server for much beyond services. This’ll ensure when you create new functionality, you know where it goes. When revisiting already created functionality, you know where to look for what.
Multi Device
One thing that isn’t mentioned much is MVC frameworks can help with mutli-device strategies. People attribute that to HTML5 and make the assumption you’re coding things in a DRY manner and they can be easily reused. Taking your application logic, data/validation, and services OUT of the presentation layer (what the user see’s) allows you to test & scale each in isolation.
What usually doesn’t change is you need all/most/some of the same exact code on a phone as you do the tablet and desktop. While the GUI can change, whether a different HTML and CSS template used for different screen sizes, your core application & business logic does not. Backbone especially with it’s use of the PassiveView pattern makes it (mostly) easy to change the underlying html or templates you’re using. Even if that code isn’t DRY because of a hard deadline or a challenging design, all your models and services stay the same helping to somewhat mitigate the problem. There are a variety of responsive design techniques you can use to not really change anything.
Server-Side vs. Client Side
This one is easy. If you’re development shop primarily does their websites and/or applications server-side, there are a lot of wonderful frameworks out there that are already “Backbone on the Server” as it were. Additionally, they have the library support just like JavaScript does to fill in the gaps. These include Ruby on Rails, Django for Python, Java’s Play Framework or GWT, and good ole’ .NET.
In these instances, 90% of the code and design is done on the server, and a tincy bit of client side code or design is done. In these cases, you don’t need Backbone because you’re already doing MVC on the server and treating the entire client as the View. The criticism by some is that, even with delivery or raw HTML/CSS/JS that’s minified and gzipped by the server, you still utilize a lot more bandwidth and http requests on the client. This is not what you want to be doing with mobile. There are a variety of frameworks and design strategies to mitigate this, though.
If you don’t do server-side development, then you need something. There is a reason Rails, Django, Play, .NET, GTW, etc. all utilize some form of MVC.
Website vs. Application
Finally, what exactly are you building? To me, even though I’m still learning JavaScript/HTML/CSS stack, the delineation is pretty clear to me. Why anyone can argue, and be correct, that the same Backbone application with large amount of JavaScript and a complicated build process could be made using simple HTML/CSS, and a tincy bit of jQuery… I just use the first 5 items to make the decision.
If you have a bunch of web designers on your team, they’re more apt to inherit something and be successful maintaing it if it’s in a context they’re used to. Conversely, if you have a lot of software client developers, they’ll be more apt to maintain something done primarily in code.
If they’re server-side devs, they’ll typically have larger expectations of a technology stack even compared to client side developers… hence rather developing on the server instead.
Bottom line, if you’re just building a website with some progressively enhanced interactivity, you don’t need a framework. If you’re doing a lot of service calls, have “state” on the client, and have a lot of user interaction & things they need to input, you’re probably building an application and probably should use a framework.
Events
Many frameworks have a messaging system that’s independent of the stack it’s on. This is also sometimes called the “event bus”. Cairngorm has CairngormEventDispatcher on top of Flash Player Events. PureMVC has Notifications. Robotlegs injects a special base class with a unique EventDispatcher instance. Backbone, much like ActionScript 2’s EventDispatcher or ASBroadcaster in ActionScript 1, uses the Decorator pattern to add event listening and broadcasting to any JavaScript object. It relies on the Underscore library to do this. Underscore does in fact use the _ as it’s class name. Yes, there are basic ECMA level event dispatching capabilities in JavaScript, but they don’t have good cross browser support, and are harder to modify. AS1:
var obj = {}; var listener = {}; ASBroadcaster.initialize(obj); obj.addEventListener(listener); listener.alert = function() { trace("Alert!"); }; obj.broadcastMessage("alert", "Sup.");
AS2:
import mx.events.EventDispatcher; import mx.utils.Delegate; var obj = {}; var listener = {}; EventDispatcher.initialize(obj); obj.addEventListener("alert", Delegate.create(listener, onAlert)); listener.onAlert = function(event) { trace("Alert!"); }; obj.dispatchEvent({type:"alert", target: obj});
AS3:
import flash.events.EventDispatcher; import flash.events.Event; var dispatcher:EventDispatcher = new EventDispatcher(); var listener:Object = {}; listener.alert = function(event:Event):void { trace("Alert!"); }; listener.addEventListener("alert", listener.alert); dispatcher.dispatchEvent(new Event("alert"));
Backbone via Underscore:
var object = {}; _.extend(object, Backbone.Events); object.on("alert", function(msg) { alert("Triggered " + msg); }); object.trigger("alert", "an event");
Notice this uses a callback as an anonymous function. If you’d prefer a proper function attached to a class or a closure or some other object, you can do that too.
In ActionScript 3, if your events grow, you start utilizing packages to organize Event classes to their domain of interest.
In Backbone, they want you to utilize namespaces (a colon to separate noun:verb) to delineate them.
myCollection.trigger("selection:changed", myIndex); socketWatcher.trigger("user:join", userModel);
While JavaScript doesn’t support constants, many IDE’s can infer them through code hinting / intellisense like IntelliJ/Webstorm, Sublime, and Aptana. Although magic strings don’t go away in JavaScript, this is how you can mitigate the growth, make them easier to test, and prevent misspelling bugs.
You can also trigger many events in 1 call by adding a space.
book.on("change:title change:author", someCallbackFunction);
Since JavaScript has the same stupid scope challenges that ActionScript 1/pre-Delegate, you can optionally provide a scope (called a context) as the 3rd parameter.
model.on('change', this.render, this)
Unlike ActionScript, the Underscore event system allows you to hear about all events via “all” which is basically a reserved word/event name.
var logger = {}; logger.on("all", function(eventName) { console.log("Event: " + eventName); });
Notice here I’m using console which is basically JavaScript’s trace. This breaks IE. Don’t do it. Use log4javascript unless you’re a WebKit Only Kid.
To remove events, in Flash you just use removeEventListener:
myListener.removeEventListener("alert", onAlert);
Using Underscore, they have a lot more options here.
//();
Finally, dispatchEvent is to trigger. ActionScript:
object.dispatchEvent(new Event("myEvent"));
Underscore:
object.trigger("myEvent", arg1, arg2, etc);
You can pass as many arguments as you want, and they’ll get passed in.
Take a hint from a guy who survived AS1 and AS2: pass an object as the first and only parameters; it’ll make your API more flexible and is an easier convention to follow vs. being forced to use event order from 2 disparate places in code.
Let’s review.
ActionScript: EventDispatcher
Backbone: Underscore.js
ActionScript: addEventListener
Backbone: on
ActionScript: removeEventListener
Backbone: off
ActionScript: dispatchEvent
Backbone: trigger
ActionScript: MyClass extends EventDispatcher
Backbone: _.extend(MyClass, Backbone.Events);
Backbone.Model
A Backbone Model is basically a PureMVC Proxy. That is, a Model that handles both access control to your data, change events via the Observer pattern, and it wraps a service layer to some web service that the Model’s data maps to on the back-end.
In Cairngorm it’d be basically the same thing. In Robotlegs it’d be a Model that has a Service injected into it. In Swiz, Parsley, and even in Robotlegs, the Model doesn’t usually contain validation, nor a service(s) with error handling. Sencha’s ExtJS and Touch do this as well with their Models, and it’s a common theme you’ll see with a lot of JavaScript frameworks. Doesn’t make it right, just something you’ll have to wrap your head around. Think a Rails ActiveRecord on the client with the ability to be mapped to various services; like localStorage vs. an actual Ajax call to some web service.
Additionally, it’s implied they are a single entity. Usually the whole point of Models or ValueObjects in ActionScript was to create a combination of data from both what the middle tier provided, but also things the client needed as well. There wasn’t always such a tight coupling between a database table and the Model you’re viewing. This is how Backbone Model’s are viewed.
Assuming you’re creating a framework-less Model in ActionScript, you’d go:
package { import flash.events.EventDispatcher; class Person extends EventDispatcher { private var _firstName:String = "Jesse" public function get firstName():String { reutrn _firstName; } public function set firstName(value:String):void { if(value !== _firstName) { _firstName = value; dispatchEvent(new Event("firstNameChanged")); } } function Person() { super(); } } }
If you’re using Flex:
package { import flash.events.EventDispatcher; class Person extends EventDispatcher { [Bindable] public var firstName:String = "Jesse"; function Person() { super(); } } }
Or PureMVC:
package { import flash.events.EventDispatcher; class PersonProxy extends Proxy { public static const NAME:String = "Person"; private var _firstName:String = "Jesse"; public function get firstName():String { return _firstName; } public function set firstName(value:String):void { if(value !== _firstName) { _firstName = value; sendNotification("firstNameChanged", _firstName); } } function Person() { super(NAME); } } }
In Backbone it’d be:
Person = Backbone.Model.extend({ firstName: "Jesse" });
Notice how you’re “extending” a Backbone.Model. Classes & inheritance in JavaScript are a rabbit hole (all the things ActionScript 1 fixed in ECMA are still not fixed in JavaScript… not even in Chrome nightly builds), so for now let’s just pretend it’s what you think it is.
For working with Backbone Model’s, you use the get and set function convention. Sencha’s ExtJS and Touch do this as well. Meaning, you do not set things like this:
// Wrong var person = new Person; person.firstName = "Cow";
Instead, you set it:
// Correct var person = new Person; person.set("firstName", "Cow"); // or object syntax person.set({"firstName": "Cow"});
The set method will trigger a “change” event automatically dispatched (triggered) from your Model. If you pass a
{silent: true}
as the 2nd parameter, it won’t trigger the event.
These events are automatically name spaced to the properties that changed. For example, in Flex, the convention was “propertyName” + “Changed”, like so:
[Bindable(event="firstNameChanged")] public var firstName:String = "Jesse";
In Backbone, while the change will fire for anything, if you’re just interested in a specific property, you can do the same thing:
person.on("change:firstName", function(model, firstName) { log("Person's change firstName, model: ", model, ", firstName: ", firstName); });
You do NOT have to define these yourself like you do in Flash/Flex, it’s done & triggered for you automatically based on the property names on your model.
Like Sencha’s ExtJS, the Models have a validate method that can be applied on a per property basis. If the validate method is there, it’s run. If it doesn’t validate your change, the property isn’t set, and the change event isn’t triggered, and an error event is triggered instead. You can either listen for these, or pass an error callback into the set method itself.
A Model has a lot of other methods only server-side people care about. The only ones you’ll care about are fetch, parse, toJSON, and isValid.
Now for the scary things:
- Model’s have a fetch function that creates a jQuery XMLHttpRequest instance to populate your model from some url. Basically a lighter weight HTTPService (or nice wrapper for URLRequest).
- A built in parse method which defaults to smartly reading JSON responses. In Flash/Flex, we’re used to either using a Factory class, or a ValueObject that knows how to “build itself”.
- A save method that does the same, asking jQuery to serialize and save your updated Model.
One last thing to note that is mentioned in the 2nd bullet. ALL parsing is basically done in the Model. It’s assumed your middle tier is giving you JSON (bad assumption for larger companies who are infatuated with XML. Yes, I know it’s 2012, they don’t). You have the option to override the parse function and parse it however you want, like in the case of using XML.
This, as opposed to using Factories inside of your Service class, or using self-building ValueObjects. Then placing the results in your Model… vs. your Model class doing everything.
Individually these are strange, but ok. Together, they’re whack. A mash of functionality, at least in Flex projects, I’m used to separating out… even without unit tests. You don’t have to use these functions they provide for you, but it’s very clear they did a lot of work making it easy as pie to use Backbone with someone like Ruby on Rails without the client doing a lot of work.
Either way, the jQuery ajax calls wrap HTTP requests really well, so it’s not like you can’t introspect what’s going on. Callbacks work and Safari/Chrome/Firefox have better-than-Flash/Flex networking debugging tools as well.
Backbone Collections
Some brief notes about Backbone Collections. Like Sencha’s Ext JS, Model’s have an implied one to one relationship with some back-end service that populates your Model class. Collections are basically a bunch of Models. So if you get a “list of Contacts”, in Backbone it’d be a “Collection of Models”.
Backbone uses Underscore to add 50 billion useful array methods to it so working and filtering your Model lists is easier. The “nice” feature of Collections is you can set the Model that it contains, and all our add methods will automatically convert the JSON you pass to it. :: shrugs :: I’d rather have unit tests verify these access points, or even… you know… strong typing. Still, there’s tons of value if you’re using formalized JSON; I’ll show you in a second.
Collections have a lot of the same methods as a Model for interacting with the server, parsing, etc. This is where setting the Model of the Collection handles the parsing/populating for you.
Backbone.sync = function(method, model) { alert(method + ": " + model.url); }; var Accounts = new Backbone.Collection; Accounts.url = '/accounts'; Accounts.fetch();
The fetch method is a Model & Collection method for “go get data from the server at my url”. It’s like HTTPService’s send method or URLRequest’s load. It creates a jQuery XMLHTTPRequest internally. For those interested, I believe it’s using the multiple concurrency option (as opposed to first/last which you could configure for Flex HTTPService).
Notice if the Accounts model property is “Account”, then whatever comes back from the server will automatically be parsed to an Account Model. The url is the web service URL.
Also rad is the sync method. There’s a cool infrastructure here that you can read more about on Backbone’s site. Suffice to say they’ve abstracted serialization of your Models, whether you’re saving to a database or LocaleStorage (the string key value store you get in browsers that has more space the Flash’s SharedObject). This makes caching, mocking, and testing a lot easier.
One thing I found curious is your Model’s have a validate function but Collections do not. My guess is it’s trivial to use a forEach with a validate call, so they saw no need to add. Most Flash/Flex developers don’t really write these as much as they should. You usually either let the server guys code tell your data is bad, you use unit tests, or hope strong-typing finds problems when creating ValueObjects from UI controls. Sencha follows this validate mentality as well.
Router
Web applications are unique in that they have an address. Some even have an address that represents a state they can exist in. There is often a symbiotic relationship between the URL and the application. A different URL can put the application in a different state. Conversely, the application state is reflected in the URL.
The format, however, varies. The short of it is, everyone likes slashes, but # are used for backwards compatibility for those browsers that don’t support the History API (basically IE 9 and below). Backbone’s Router abstracts this for you.
If you’ve ever utilized Gaia to build a Flash website, you’ll know exactly what to use Backbone Router for. Sort of. Instead of mapping your web site’s pages, you’re mapping both page URL’s as well as URL query parameters to run functions. Yes, you could have those functions change a page, or just draw a new one. Gaia handled all of this internally including branching, hijacking, and URL modification. Backbone Router does a lot of the same but is a little lower level so you to do a little more work.
There are basically 2 features you can utilize. The first is when you go to a different section or state in your application, you can update the URL in the web browser.
The second is when the URL changes. Whether the user modifies the URL in the url bar, they press the back or forward button, or even if the user comes to the URL via a book mark or copy pasta. All can trigger a callback function for each type of URL so your application can get itself into the proper state, get the data it needs, or whatever else to correctly represent the URL.
Notice the routes hash below:
var Workspace = Backbone.Router.extend({ routes: { "help": "help", // #help "search/:query": "search", // #search/kiwis "search/:query/p:page": "search" // #search/kiwis/p7 }, help: function() { ... }, search: function(query, page) { ... } });
You extend the Router class, and then define the routes in the URL, and what callback function to call. While straightforward, you can use all kinds of string matching for more dynamic URL’s. Internally, those routes will call addCallback to initialize it. When you want to update the URL, just call navigate.
Using the Backbone.history class, you can monitor change events, support the back button in your app, etc.
View
A Backbone.View isn’t a View that a Flash or Flex Developer would call a View. It’s actually a Controller class on top of a Passive View that allows your GUI to have a less coupled integration into Backbone. All your HTML and CSS, whether made by hand or using a templating library like Mustache or Handlebars, has no code in it.
All the code that updates via the Models is done in the View.
All the code that listens for user gestures is done in the View.
So it’s the Controller in the Passive View pattern. Make sense?
I’ve seen 2 people utilize Robotlegs Mediators like this. One was a colleague who’d create MXML with zero ActionScript in it. Just like you do in HTML, he’d assign click handlers to MXML component id’s (or Sprite/MovieClip names) all in the Mediator. When his Model Actors would send out change events, he’d use the injected Models to update the View’s data usually with intimate knowledge of the view’s structure since it didn’t have any getter/setters, nor binding, to help with the drawing.
PureMVC could be done in the same way. There was a sect of people in the early Cairngorm days who used a ViewModel in a similar fashion.
A Backbone View was specifically created to handle HTML and CSS, and as such, has a LOT more references than just the passive view reference. Those are:
- el
- id
- className
- tagName
- attributes
Here’s a tiny Robotlegs Mediator:
package { public class AvatarViewMediator extends Mediator { [Inject] public var view:AvatarView; [Inject] public var model:PersonModel; public override function onRegister():void { addContextListener("onPersonModelChanged", updateImage); addViewListener("onImageClicked", onImageClicked); updateImage(); } private function updateImage(event:Event=null):void { view.image = model.userImageURL; } private function onImageClicked(event:MouseEvent):void { dispatch(new Event("onAvatarImageClicked")); } } }
When the ActionScript GUI class is created, it’s Mediator is created. It immediately updates the Avatar GUI with the latest user image. If the Model ever changes thereafter, the image is immediately updated. Finally if the user clicks on the image, it’ll dispatch an event on the frameworks event bus to let someone else handle it; maybe a Command, maybe another Mediator… etc.
Here’s the equivalent in JavaScript:
// make a global event bus like Robotlegs, or PureMVC's Notifications eventBus = _.extend({}, Backbone.Events); Backbone.Model.prototype.eventBus = Backbone.Collection.prototype.eventBus = Backbone.View.prototype.eventBus = eventBus; var personModel = new PersonModel; var AvatarView = Backbone.View.extend({ id: "avatar-div", initialize: function() { this.model.on("change", this.render, this); }, events: { "click .avatarImage": "onImageClick" }, render: function() { var imageURL = this.model.get("imageURL"); $(this.el).html(this.template(' <img src="<%= imageURL %>" alt="" />')); return this; }, onImageClick: function() { this.eventBus.trigger("onAvatarImageClicked"); } }); var aView = new AvatarView({model: personModel});
Using Underscore, we listen for change events from our Model. Also notice View’s are assumed to have 1 Model. If you’ve seen any reasonably sized Robotlegs/PureMVC app, you know this isn’t often the case, heh. It’s assumed this is not null when the View is created.
The click handler listens for a click event from our HTML div. Our render function, run when the View is shown and when our Model updates, draws itself.
Notice the id is just a string; I actually use the el, or “HTML element” to set its html. The template method is an Underscore method to do cool stuff; suffice to say it refreshes the div with a newly updated Image tag with our correct URL from the model. The purists will decry this, saying that HTML should never be put into View’s and instead you should use a template library like I mentioned above.
Ignore the return this in the render function. That’s for chaining support, a method for getting functional programmers off, making code unreadable, and helping the browser swallow exceptions.
And now for the hack. You’ll notice if I want a global event system like Robotlegs, you just create your own eventBus, and shove the mofo on Backbone’s prototype. Here’s a longer explanation if you care about why. That allows you to then send the application level event in the onImageClick callback.
One thing for those of you who remember ActionScript 2: notice the 3rd parameter of of the model.on. This ensures the callback is executed in the correct scope. It’s basically ensuring you don’t have to use Delegate.create. These are pretty hard to debug, too, since you’ll sometimes get “method is undefined” methods which aren’t true; it’s actually a null pointer exception since this refers to the function vs. the class. Just be sure to always set it.
Dependency Injection
Notice that all the dependencies, in this case the PersonModel, is already created and passed into the View’s constructor. In larger applications, this isn’t always the case, or gets unwieldy. There are various Dependency Injection frameworks out there to help with this, such as Wire.
Robotlegs handled this via Dependency Injection as well as through Command startup conventions. Basically, as your application grew, you’d offload a lot of the startup to wiring up your dependencies, or instantiating them yourself if they required something more than a “new Thing”. Things such as “MainContext” would become “StartupCommand” which then broke off into “InitializeModels”, “InitializeMediators”, which even later broke off into “InitializeProductModels”, etc. You had a refactoring path.
You’ll eventually want to do the same with Backbone as your applications grow, especially if your Collections get kind of large. The browsers have a lot better libraries & functionality to serialize & read your data back compared to Flash Player in the browser, so it’s not always a huge deal to save your data to disk. They also destroy easier too.
Here’s how you configure a Singleton OmnitureModel in Robotlegs:
// ApplicationContext.as injector.mapSingleton(OmnitureModel);
Here’s how you do it using Wire:
// ApplicationContext.js omniModel: { create: { module: "models/OmnitureModel' } }
Wire also supports a modicum of binding expressions so if you need some extra data initialized in your created class, whether constructor arguments or not, you can do so. This is invaluable for Backbone View’s who often need at least 1 Model reference when they boot up.
Although I’m still learning DI in JavaScript, I found Wire helpful in configuring Backbone Models to work with local data sources instead hardcoded service URL’s. You just create a locale application context js file (like a new Robotlegs Context file) to configure the application to use locale data sources instead. So far I’ve been only able to get this to work in a few browsers as you’ll see below.
Local File Access
Flash was nice in that it had a localTrusted security sandbox later with a unique per system file for mms.cfg configuration to allow secure sandboxes. Browsers aren’t like this.
Firefox requires some config changed to allow this.
Safari and Chrome still want you to launch it in a certain mode.
I still haven’t figured out IE.
I’ve been using Charles for mapping to local directories on my machine. This is used to fool the browser into working, even for production URL’s, so should work in IE when I have more coffee.
John Paul suggested I use Underscore’s wrap function as another alternative which I haven’t tried yet.
Modules / Packages / Dependencies
As your application grows, you’ll quickly outgrow initializing everything in your index.html; ie you’ll start to get a lot of classes. While Backbone comes with Underscore to give you basic inheritance beyond Object.prototype, you still have no package solution like ActionScript 2/3/Java/Ruby/Python/PHP/Corona/C#… (hell, everyone but JavaScript) give you.
Additionally, you don’t have a dependency management solution; you have to manually control who’s loaded, and when. That is simply not feasible in a reasonably sized JavaScript application, even with 1 developer vs. 4.
This is where RequireJS comes in.
Considering Require has to account for how you create “classes” in a language that provides basically (lol, basic… yeah… heh… sigh… :: takes a shot ::) 3 ways to do so (Object.new, Object.prototype, and closures), various libraries that have their own dependencies, as well as how all libraries handle putting themselves on the global objects (ie window, document, etc.) differently… Require has a lot of work to do on just cleaning up the language.
ActionScript 1 solved part of this via the #initclip / #endinitclip pragmas. You’d put those at the top of your class with a number, like #initclip 3, and you could then control the load order of classes.
ActionScript 2 solved the packages part by building packages that compiled down to object dot notation, and abstracted both that as well as the #initclip stuff away from developers.
ActionScript 3 gave you namespaces with traits to speed up prototype chain lookup speed.
JavaScript has none of that.
Require tries to do all of that. And then some, such as optimization. Just read the why and you’ll get it. Backbone apps work just fine with Require, and I encourage you to do so to ensure your application can reasonably scale on a multi-person team right out of the gate.
Here’s how you use Robotlegs in a class within a package (set of nested folders):
// MainContext.as package com.jxl.minecraftrecipes.rl { import com.jxl.minecraftrecipes.rl.mediators.FavoritesRecipeViewMediator; import com.jxl.minecraftrecipes.rl.mediators.RecipeViewMediator; import flash.display.DisplayObjectContainer; import org.robotlegs.mvcs.Context; import views.FavoritesRecipeView; import views.RecipeView; public class MainContext extends Context { public function MainContext(contextView:DisplayObjectContainer=null, autoStartup:Boolean=true) { super(contextView, autoStartup); } public override function startup():void { this.injector.mapSingleton(FavoritesModel); mediatorMap.mapView(RecipeView, RecipeViewMediator); mediatorMap.mapView(FavoritesRecipeView, FavoritesRecipeViewMediator); super.startup(); } } }
Here’s how you do the something similiar in Require:
// config.js (function() { require.config({ baseUrl: "./js", optimize: "none", paths: { "../../libs", handlebars: "../../libs/handlebars/hbs" }, packages: [ { name: "underscore", location: "../../libs/underscore_132", main: "underscore", }, { name: "backbone", location: "../../libs/underscore_132", main: "underscore", } }); }).call(this);
Basically configure the root directory (“js”), set some paths so you can use them either as additional source paths, or short cut names (like I did with handlebars), and finally determine your packages: where they are and what their names are. Keep in mind Require has a strict coding convention for what packages are.
Optimization
When you compile a Flex or ActionScript application, here’s basically what happens:
- all MXML is converted to ActionScript
- all ActionScript is converted to ABC byte code (small, procedural)
- ABC is converted to MIR (a language JUST above machine code; easier to target different chipsets & optimizations in MIR)
- some MIR is converted to native code, some is compiled natively via JIT, and some just chills out
- all of that is compressed via gzip with additional optimizations to shrink it to a small file size (remember, it was made this way when modems ruled t3h interwebz and bandwidth was tincy and faulty)
- images are compressed, whether using PNG or JPEG
- audio files are compressed (ADPCM, MP3, Speex, etc)
- fonts are converted to 1 of the 3 font engines, sometimes decreasing file size as only certain glyphs are converted to specific vector graphics)
- any other file is embedded and gzipped
- all this is put into a single SWF file, usually on frame 2, with a class on frame 1 to show a preloader and “new” frame 2.
Why is this good? You get an ultra compressed, self-contained, single HTTP request file with all dependencies delivered to the client in a streaming fashion. And it works. This is why we Flash Devs never got JavaScript’s eval, because our code is actually compiled. JavaScript in the browser has none of that.
- All files are un-optimized text. Seems trivial, but for large applications, this can add up. For smart phones and tablets, while sometimes fast, this costs people money.
- Since there are multiple files, this is multiple HTTP requests. These take both time to create/destroy, and elongate how long it takes for your application to start and/or transition to a new section. This is really bad on some mobile devices, not just desktop. It’s REALLY fun to use these apps on slow VPN.
- If a class isn’t loaded let and you try to use it, things break.
- Browsers love to swallow null pointer exceptions making the above really challenging to debug.
- While you could theoretically remember to load certain files in certain orders, God help you if you ever have to, heck, WANT to refactor. On a multi-person team, even with Git, you’re eff’d.
Require has a variety of optimization tools that basically combine all your files into a single JS file, strip out all whitespace, convert all your variables to 1 letter variables, amongst other things. Basically makes your code unreadable, too; obfuscation isn’t the goal here, though. There are many others too like JSMIN and Yahoo’s YUI, many of which you can use in a browser.
Conclusions
As you can see, Backbone has a lot of the core features you need to build applications in JavaScript. It has Models that follow the typical Observer pattern of holding your data and broadcasting messages for those who care to know about changes. It has View’s (which act as Robotlegs Mediators or the Controller on top of PassiveView HTML) allowing you to handle the traditional race condition of “my data is ready, but my view is not” vs “my view is ready but my data is not” by both accessing Models directly and listening for messages via Underscore’s messaging system which you can make a global event bus if you wish. Finally, it’s Router allows you to marry your application as a state machine against the browsers’ URL bar to support both deep linking, back/forward navigation, and history.
Riding on top of Underscore, it ensures you have all the basic necessities you need for making JavaScript easier to use. Although, I still think if you’re going to utilize JavaScript swapping out dynamic HTML for Views, then you really need a good templating engine such as Handlebars or Mustache.
The current pain point I’m still working on and haven’t figured out yet is unit testing Backbone models. On reasonable sized Flex projects that have issues, my first order of business is to break out the parsing logic and the service logic into separate classes so I can create them as units to be easily tested. This copious refactoring continues until “a Model” consists of a variety fo classes. Backbone puts all of them together into 1 class and with browsers having really strict local file access rules + the fact services are actually inside of it via jQuery Ajax calls, it has made it challenging to fake their service calls to fixtures/mocks… and test only 1 thing. Anyway, I’m still learning.
Hope this helps!
14 Responses
Very nice article – thank you.
One question: event handling (trigger, on, off) thats jquery not underscore?
/dennis
Thanks! I believe it’s actually just a Backbone Events utility. Check around 68 of their source.
Great write up, I do miss flex sometimes but I’ve been getting the hang of all of this “new” stuff
Wish I took the dive into html/js sooner, but I oh so love AS3.. oh well
re: console.log I think its fine to use as long as you are using a compiler, they will remove those console statements. So for dev you have your logging and for production it is turned off. However if you want logging in production, well of course your idea is good
One thing to check out is Almond.js it makes an even smaller footprint, by same author as require.
I know we are all programmers, but its worth a mention that eventually we have to deal with UI, even if only on the admin side of things. For that a great place to start is Twitters bootstrap:
Combine that with less.js and you will have a very nice dev environment. For our team we are using an environment variable (development, production, etc) and if the backend detects its being run in production we serve the compiled almond and compile css, but for development we load js through require.js and load our less files directly with less.js
Nice article.
I’m still leaning towards using Ember instead of backbone though, because at least you can use bindings to update the view automatically when the model changes, which saves a ton of fiddly jQuery expressions…
Thanks! Yeah, Angular has the same. Bindings have already proved their worth in Flex if done correctly so I totally agree.
Nice article Jesse. To Tom and other readers possibly evaluating backbone.js, please note that backbone.js purposefully tries to remain as “core” as possible. By doing so, it lets extensions compete and be interchangeable. The side-effect of this is when you see laundry lists comparing capabilities among frameworks it will tend to only show backbone’s core and therefore a relatively weak contender. Fortunately, backbone extensions are easy to come by. In the case of model-view binding, there’s a fantastic extension called ModelBinder you’ll find here:. You should also at least check out Backbone Relational and Marionette.
I have no significant affiliation with Backbone except that I use it, blog about it, and provide feedback.
Thanks for the linkage, Aaron!
Nice article. I prefer Backbone to Ember or Angular, while all 3 are rock solid projects, I still think being (boilerplate) free when it comes to JavaScript (even for large apps) is a major plus for Backbone, for me the perfect combo is Backbone on CoffeeScript
I sometimes envy the authors of articles like this because the underlying theme is that the workplace should be logical and about business and making money and so let us find a frameworks that helps. Sometimes I find that too but sometimes… My current employers for example are inventing ways to fail that I had never heard of. Never even considered. It takes real skill to fail in such a way that every bit of the fail is the worst fail possible like a fractal tree. I am only passing through but in an act of generosity I may suggest to them a framework. So do you know of an anti fail framework that I can recommend to them? Just something to stop them driving off a cliff will do
A lot of us have been there, anon. We feel, and know your pain. Hang in there, fight to get as close to the ivory tower as you can, and don’t take it personal.
Very interesting article, definitely open my eyes to alternatives. Thanks Jester XL
Love your description on chaining: “Ignore the return this in the render function. That’s for chaining support, a method for getting functional programmers off, making code unreadable, and helping the browser swallow exceptions.” Great article by the way, just what I was looking for. I use AS3 Signals which I’ve abstracted into a SignalsManager class, and having read this article enlightened me that I have been implementing the Event Aggregator pattern all this time (something I knew I wanted in JS).
I’m just a beginner wandering this maze of alternative javascript frameworks – but don’t understand why more people from AS3 aren’t turning to haxe as a cross browser solution. Does it have some issues that I haven’t grasped yet?
Client adoption. You have thousands of web developers who code JavaScript, and a few hundred who espouse CoffeeScript as a “step in the right direction”.
This, as opposed to those saying, “See, Google built Gmail with Java via GWT.” and therefore helping to validate the need for a strongly typed language like Haxe.
I get it, but not enough people do, nor do we have a Joel Hooks style evangelist for Haxe.