Composite JavaScript Applications With Backbone And Backbone.Marionette

Although I’ve mentioned it in this blog already, and have been tweeting about it, we’ll call this the official announcement for my new Backbone.Marionette library.

Backbone.Marionette: Make your BackboneJS apps dance with a composite application structure!

Why?

Over the last … however many months I’ve been using Backbone, I’ve developed a number of opinions around building apps. I have a particular style of code that I write, with a particular set of functionality that is common through most of (if not all of) my apps. Backbone.Marionette is another of the many plugins I’ve created, that encapsulates my opinions.

To date, i have the following libraries for Backbone, with more ideas in my head based on the work I’m currently doing:

My goal with these plugins is not to say “this is how you must work with Backbone”. Rather, I want to provide options and opinions for those that are running into the same problems that I’ve run into. When I find myself solving the same problem over and over again, I find myself wanting to extract the solution into a library. This lets me get on with my real application development instead of focusing on solving the same problem again.

The trick with my plugins, is to provide a set of libraries that all work independently, but can be combined in very creative ways to create some even more amazing. Even within each library, I’m trying to take an approach that allows you to use only the parts that you want. Of the three plugins I’ve written, I think ModelBinding is the most restrictive / hand-holding. Memento and Marionette both offer a great deal of freedom and flexibility vs the configurability of ModelBinding.

What?

Marionette is a library of tools that you can use when you want to, without being forced to use every single piece of it. These tools include:

  • Application initialization
  • View management
  • Event aggregation

Though the number of pieces is currently small, each of these pieces is very flexible and can be used without requiring any other piece. They can also be integrated into existing applications as-needed, allowing you to migrate an app from your existing code to Marionette.

How: Application Initialization

This was the big piece for me in building Marionette. I noticed that at some point that my BackboneJS applications tend to gather a cruft of procedural mess in an “application object”. This application object has always been responsible for starting up the various bits of the app: routers, initial views, instantiating collections and initial models, etc.

The problem is that these objects have usually ended up in a giant tangled mess with far too many concerns. For example, here’s some code from an image gallery that I wrote a few months ago. Note that this is only the application startup code:

ImageGallery.App = function(initialImages){
  var vent = _.extend({}, Backbone.Events);

  var images = new ImageGallery.Images(initialImages, {
    vent: vent
  });

  var mainView = new ImageGallery.MainView();

  var controller = new ImageGallery.Controller(images, mainView, vent);

  var router = new ImageGallery.Router({
    images: images,
    mainView: mainView,
    controller: controller
  });

  var showImage = function(image){
    controller.showImage(image);
    router.navigate("/image/" + image.id);
  }

  vent.bind("image:selected", showImage, this);
  vent.bind("image:edit:cancelled", showImage, this);
  vent.bind("image:edit:saved", showImage, this);
  vent.bind("image:deleted", function(){
    controller.home();
  });

  vent.bind("image:add", function(){
    controller.addImage();
    router.navigate("/add");
  });

  vent.bind("image:next", images.next, images);
  vent.bind("image:previous", images.previous, images);

  vent.bind("image:edit", function(image){
    controller.editImage(image);
    router.navigate("/edit/" + image.id);
  });

  this.initialize = function(){
    var imageListView = new ImageGallery.ImageListView({
      collection: images
    });
    $("#image-list").html(imageListView.render().el);

    new ImageGallery.MenuView({
      vent: vent
    });

    Backbone.history.start();
  }
}

It’s a giant mess and it’s difficult to understand and maintain.

The solution involved recognizing that I was putting far too many concerns into a single place, combined with a healthy dose of encapsulation. I want each functional area of my application to have it’s own start up code, encapsulated within that functional area’s code. I don’t want to have to mash all the functional areas together into one procedural mess. So, I build the `Backbone.Marionette.Application` object.

This object provides a number of different features, one of which is the ability to register application initialization callbacks. To do this, you need to create an instance of an Application object, first. Then call the `addInitializer` method and provide a callback function. The initialization functions are then kicked off when you call the `start` method on your application:

// create your application instance
MyApp = new Backbone.Marionette.Application();

MyApp.addInitializer(function(){
  // do stuff here, to kick off your application
});

MyApp.addInitializer(function(){
  // more start up stuff here
});

// run all of the initializers!
MyApp.start();

You can optionally pass an object through the start method, as well. This object is made available to all of the initializer callbacks as a single parameter to the callback function. Additionally, there’s a “initialize:before” and “initialize:after” event that the application object raises, using Backbone’s Event functionality.

The trick is to keep your code organized and put your initializers next to the code that they initialize. Don’t just cram all of your initializers into a single file, recreating the same mess from my image gallery. Put your initializers near the code that they initialize. Keep them separate, keep it clean and decoupled. Add as many initializers as you need. Just remember that you have no guarantee of the order in which they run.

For more info on the application object, see the documentation.

How: Event Aggregation

The `Application` object comes with an event aggregator built into it. You can call the `.vent` property of any Application instance and have full access to the Backbone Event system. I won’t go into any more detail about this right now, as you can read up on the basics of how I use event aggregators in my previous blog post.

Of course you can still build your own event aggregator with one line of code.

var vent = _.extend({}, Backbone.Events);

In fact, this is all I’m doing in the Application object. I just put it there as a convenience so I don’t have to create one manually for every app:

How: View Management

In one of my recent blog posts on composite JS apps I talked about the use of a region manager, and the code that I wrote in that post has been migrated into Backbone.Marionette as the `RegionManager` object.

The intent and purpose of a RegionManager is the same as I’ve previously talked about. The difference in Marionette is how you access a RegionManager. You have two options: use the `addRegions` method on your Application instance, or manually create a RegionManager object. The choice gives you flexibility, allowing you to use a RegionManager without using the rest of Marionette, if you want to.

The `addRegions` method on the Application object accepts a single parameter of a JavaScript object literal. The keys for this object become the names of the regions, and the value of each key should be a jQuery selector that points to the HTML DOM element that your region manager will manage:

MyApp.addRegions({
  navigationRegion: "#navigation",
  mainRegion: "#main-content"
});

You can also pass a RegionManager definition as a value. See the documentation for more info on this.

Once the application is started, each of the keys that specified will be available on the application object instance. You can then call the `show` method on your region managers to show your Backbone views in that region.

Documentation And Source Code

I’ve linked to the documentation several times, which is found on the Github repository that houses the code:

http://github.com/derickbailey/backbone.marionette

I also have the annotated documentation that I’ve previously talked about, available at:

http://derickbailey.github.com/backbone.marionette/docs/backbone.marionette.html

BBCloneMail: A Reference Application

In addition to the source code and documentation, I’m building a sample application that can be used as a reference for building Backbone applications with Backbone.Marionette. The name “BBCloneMail” comes from the idea of a “Backbone clone of GMail” to demonstrate a composite application. Though it’s styling is different than Gmail’s, you can clearly see the influence in the layout, the use of categories (labels) and the drop list to switch between the mail and contacts apps.

You can find BBCloneMail online at:

http://bbclonemail.heroku.com

The source code is here:

http://github.com/derickbailey/bbclonemail

And the annotated source as documentation is here:

http://derickbailey.github.com/bbclonemail/docs/bbclonemail.html

But Wait! There’s More!

This is the first official release of Backbone.Marionette: v0.1.0… I expect the functionality to continue to grow and evolve as I use it in more application, and as (hopefully) I see other people using it and contributing their own needs to the code.


Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Derick Bailey

Derick Bailey is an entrepreneur, problem solver (and creator? :P ), software developer, screecaster, writer, blogger, speaker and technology leader in central Texas (north of Austin). He runs SignalLeaf.com - the amazingly awesome podcast audio hosting service that everyone should be using, and WatchMeCode.net where he throws down the JavaScript gauntlets to get you up to speed. He has been a professional software developer since the late 90's, and has been writing code since the late 80's. Find me on twitter: @derickbailey, @mutedsolutions, @backbonejsclass Find me on the web: SignalLeaf, WatchMeCode, Kendo UI blog, MarionetteJS, My Github profile, On Google+.
This entry was posted in Backbone, Composite Apps, Javascript, Marionette. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Anonymous

    This looks pretty useful, seeing as how right now I can’t decide how to handle creating a new “region” for my app (listing a library of items and providing a search interface).

    One question, how does your RegionManager differ from Backbone.LayoutManager? I haven’t looked into that too much but I thought it does pretty much the same? Sounds like you can choose to use that along with Marionette anyway, though.

    • http://mutedsolutions.com Derick Bailey

      I looked at LayoutManager a week ago and really liked what I saw. By the time I started playing with it, though, I already had a lot of code in place for the project that Marionette came out of. I spent an hour or two looking at LayoutManager to see if I could replace what I had. I think I have a few different opinions on how I want to build Backbone apps, compared to Tim’s LayoutManager… so I extracted Marionette from my project instead of trying to rebuild that app on top of LayoutManager. I’m sure there’s a ton of overlap between what I started here and what Tim is doing. I really need to dig deeper into LayoutManager more and see if I’m heading in the same direction or not. 

      If anyone else is looking for a composite app structure for Backbone, be sure to check out Tim’s project: https://github.com/tbranyen/backbone.layoutmanager

  • http://www.facebook.com/tgeek Khurram Hanif

    Great work. Perhaps you should also look into http://angularjs.org 
    Yet another JS framework which I found more simple than Backbone and already ships a number of features not available in other frameworks (Composite apps).

  • http://dannydo.es/ Dan (Danny) Fellars

    Derick, I’m continuing to play with Marionette and have another question.  I’m struggling with when to use a CollectionView instead of an ItemView whose template has a {{each}} loop in the template.  Can you think of use cases where one is better than other?  It seems that if your Collection has any kind of global info (like a header to a list), then it needs to be an ItemView.

    • http://mutedsolutions.com Derick Bailey

      Hi Dan,

      Both of those options are valid – there’s nothing wrong with using the ItemView for a collection or using a CollectionView + ItemView for a collection. When you use each is the real question. 

      I’ve outlined a couple of scenarios and guidelines for this in another post: http://lostechies.com/derickbailey/2011/10/11/backbone-js-getting-the-model-for-a-clicked-element/

      The title is a bit misleading because it was in response to a specific question I’ve seen multiple times, but it covers exactly these two scenarios in the answer. There are other good reasons for when to use which, as well. That post covers a few of the basics, though.

      Hope that helps.

      • http://dannydo.es/ Dan (Danny) Fellars

        Ah, great post. very helpful. Thanks again Derick!

  • Anonymous

    Would it be possible to add a region that has been added dynamically to the body after the initial regions are defined?

    I append a DIV element as part of my app and I wanted to add a region to the RegionManager that is contained within that dynamically added DIV.

    I’ve used the addRegions method and that new region does appear in the MyApp.regions, but I’ve appeared to hit a snag with using the show method as it appears to indicate that MyApp.newRegion is undefined.  Is there a step beyond the addRegions method that I’m missing?

    • http://mutedsolutions.com Derick Bailey

      `addRegions` only works as a short cut to creating a region for you. it must be called before the “start” method, or it won’t instantiate the regions.

      You can create your own region manager instances, though. I’ve updated the documentation for Marionette to point out how to do this in the “Marionette.RegionManager” section: https://github.com/derickbailey/backbone.marionette

  • daman

    I am trying to build a complex app using Backbonne.Marionette with Boilerplate.

    I am confused of how to use controller. The Backbone.Marionette.Layout has been used in the App.initAppLayout function in app.js

    the controller is handling the route functions. i want to use controller function to
    show view in a region. there are three regions – navmenu, content and footer
    Please advise how I can attain this.

  • Dale Anderson

    G’day Derick, this is cool stuff.

    I’ve written a composite Javascript library built on top of knockout.js. For loose coupling, it uses a pubsub engine for communication between components. It works pretty well and is being used in a fairly sizeable product (yet to be released).

    http://danderson00.blogspot.com.au/2012/08/introducing-knockoutcomposite.html

    I’d love to hear what you think!

  • Mohsin

    I want to start a new application with merrionettejs and twitter bootstrap. Can any body tell me that is this a safe approach and if yes then from where I should start.

    Waiting for reply!!!!

    • http://mutedsolutions.com Derick Bailey

      Hi Mohsin,

      I use Backbone, Marionette and Bootstrap together all the time. There’s nothing special to do. Bootstrap is mostly just CSS, so just use it the way you would any other CSS with Backbone/Marionette.

      • Mohsin

        Thank Derick for your quick response :)

        Can you advice me on how to get Marionette code minified? also if there is any boilerplate code ready to use and minify/compress after dev as here https://github.com/BoilerplateMVC/Marionette-Require-Boilerplate.

        • http://mutedsolutions.com Derick Bailey

          the downloads on http://marionettejs.com include the minified version

          • Mohsin

            My question was about minifing after completing development with Marriontte, after writing all views, models and collections of my application. Otherwise having many files will increase load time of page.

          • http://mutedsolutions.com Derick Bailey

            oh, i see. there are a lot of tools out there. i like using grunt and uglify.js, but there are lots of others. some quick google searching should turn up tools that work well in your development environment.

          • Mohsin

            Thanks :) I think I will go with grunt http://gruntjs.com/