Latest Updates: Our Blog

Code Drop: Backbone.js

Posted
Oct 13th, 2010

Tags
Code

Author
Jeremy Ashkenas

Another little piece of DocumentCloud is now available: Backbone supplies structure to JavaScript-heavy applications by providing models with key-value binding and custom events, collections with a rich API of enumerable functions, views with declarative event handling, and connects it all to your existing application over a RESTful JSON interface.

We make heavy use of Backbone to create the DocumentCloud workspace. Your documents, notes, projects, and collaborators are all represented by Backbone models, and almost everything you see in the interface is created by a Backbone view. Hopefully the library will prove helpful to other folks dealing with JavaScript-heavy applications.

The project is hosted on GitHub; annotated source code is available, as is an online test suite.

17 Responses to 'Code Drop: Backbone.js'

Subscribe to comments with RSS

  1. Hey Jeremy,

    Backbone and Underscore are very nice. I started prototyping with them to build an MVC in Javascript. Its lightweight beats everything I tried before. Way to go!

    One thing however, the fetch, save and sync functions are not flexible enough. Only four methods are supported but our Back-end has a much richer set. Plus the URLs are built making too many assumptions. It would be better if I could define the methods and their URLs explicitly. To get around that, I found myself rewriting some of that code.

    Despite this, I will continue using Backbone – it suits well my needs.

    Keep up the great work!

    –Martin

    Martin Drapeau

    2 Nov 10 at 11:09 am

  2. Hey Martin.

    Fortunately, the URLs can be defined as you see fit. Just add a “url” method to your Model or Collection, and have it return the URL of your choosing.

    http://documentcloud.github.com/backbone/#Model-url

    The four basic REST/CRUD methods are supported out of the box by Backbone — naturally, you’ll need to extend your models with more than that (we certainly do). That said, I don’t think that Backbone *should* support more than REST, by default. Are there particular methods you think should be added to all models?

    Cheers,
    Jeremy

  3. Hey Jeremy,

    In our case the URL is directly tied to the method we use (i.e. /get_task, /update_task, /complete_task, etc). They go hand in hand. Currently the url function isn’t aware of the method. Plus the GET/POST arguments are built inside the Backbone.sync function.

    I don’t think the solution is for Backbone to support more methods out of the box, but instead to offer means for developers to register their own. And to let developers attach those to functions instead of imposing. For example, the Collection’s create function calls the Model’s save function. What if I want to do further processing before saving?

    Does this make sense?

    –Martin

    Martin Drapeau

    2 Nov 10 at 1:12 pm

  4. Sure it makes sense — that’s what “extend” is good for:

    http://documentcloud.github.com/backbone/#Model-extend

    You can use “extend” to make a subclass of Model, View, or Collection, overriding as many of the existing methods as you see fit. If you want to perform additional processing before saving, override Model#save, and insert the processing there.

    Jeremy Ashkenas

    2 Nov 10 at 1:16 pm

  5. Yes, that’s what I’m planning to do.
    Thanks for the rapid feedback,
    –Martin

    Martin Drapeau

    2 Nov 10 at 2:20 pm

  6. While writing my first prototype with Backbone, I struggled finding where to put Controller logic. In the end, I established a convention that it should always be in the View – never in the Model. That way, the model can remain generic and could be used by multiple views.
    With a little perspective, it even seems that Views are in fact Controllers and they could be renamed as such. What do you think?

    Martin Drapeau

    7 Nov 10 at 2:27 pm

  7. I’m curious if anyone has implemented both a localstorage + ajax solution with Backbone? I’m just getting started with Backbone, but would like to be able to download data from the server via ajax, save it to localstorage, and then update the views.

    Alex Grande

    9 Nov 10 at 3:09 pm

  8. I haven’t seen a LocalStorage + Ajax plugin yet. Unfortunately, LocalStorage is significantly different from a real database, and it’s a little tricky to make the same model representations work well when stored on the server as well as the browser.

    That said, there is a demo LocalStorage plugin that backs the Todos example app, here: http://documentcloud.github.com/backbone/docs/backbone-localstorage.html

    Jeremy Ashkenas

    9 Nov 10 at 3:22 pm

  9. I don’t see a destroy/delete method on a view. How are they removed from memory? This is my scenario: I have a collection of models that can be filtered. I only want to have views for unfiltered models. So at any time, I would like to create/delete views on the fly.
    Thoughts or suggestions?

    Martin Drapeau

    11 Nov 10 at 7:43 am

  10. To remove a view, you just remove it’s element from the DOM.

    To make this convention a little more obvious, I’ve added a “remove” method to the View. Here’s the patch:

    https://github.com/documentcloud/backbone/commit/b38e9167c7c0247485b46cdec36f8fbe761bd5d2

    Thanks for the idea.

    Jeremy Ashkenas

    11 Nov 10 at 9:45 am

  11. It seems unbind doesn’t work. Unless I’m doing it wrong:

    var onFiltered;
    onFiltered = function() {
    if (_.isEmpty(story.get(‘filtered’)))
    story.unbind(‘change:filtered’, onFiltered);
    return true;
    }
    story.bind(‘change:filtered’, onFiltered);

    Any ideas?

    Martin Drapeau

    25 Nov 10 at 3:20 pm

  12. Nevermind – programmer error.

    Martin Drapeau

    25 Nov 10 at 3:51 pm

  13. However it would be nice it events supported namespaces like jQuery. Because I now have multiple views looking at one same model. When I destroy one, I have to call unbinds on all the events and handlers I passed to bind. It is a pain to maintain…

    And a one() bind function like jQuery would also be nice. But that’s a nice to have.

    Martin Drapeau

    25 Nov 10 at 3:59 pm

  14. Found a bug in Events.trigger. Assuming your caller will unbind in a callback, your code fails because you assume the length of callbacks do not change.
    for (i = 0, l = list.length; i < l; i++)

    Solution is to change both for loops to this:
    for (i = 0; i < list.length; i++)

    –Martin

    Martin Drapeau

    25 Nov 10 at 4:39 pm

  15. Hi Martin.

    “trigger” does assume that the length of the events array does not change while it is being iterated over. However, your fix will cause undefined behavior in the case where you “unbind” events during a trigger — causing some events that should be fired to be skipped over.

    If you have a piece of real-world code that caused an issue with “trigger”, please open a ticket here:

    https://github.com/documentcloud/backbone/issues

    Jeremy Ashkenas

    27 Nov 10 at 12:41 pm

  16. You are indeed right. I have hit some issues.
    I opened a ticket. However the live code I have is too big to share. So in my ticket I presented a scenario where Events in Backbone break. I hope you can fix it. It is currently a blocker for me.

    Martin Drapeau

    27 Nov 10 at 4:09 pm

  17. Hi,
    I’m working on a project where we use intensively Backbone.
    Just a little thing you should add to the documentation : “_changed” is a sort of reserved keyword that one must not use as a callback function. I’ve lost hours trying to find out why I had the following error :
    TypeError: func.apply is not a function
    This exception is raised in the trigger() function by this line :
    list[i].apply(this, Array.prototype.slice.call(arguments, 1));
    when it tries to invoke the _changed callback but gets mixed up with the Backbone _changed property.
    Hope this helps others to not loose as much time as I have !
    Regards,
    Christophe

    Christophe Quintard

    14 Jun 11 at 8:58 am

Leave a Reply