Announcing Backbone.js 1.0
ashkenas.com
ashkenas.com
1.0.0 — March 20, 2013
* Renamed Collection's "update" to set, for parallelism with the similar model.set(), and contrast with reset. It's now the default updating mechanism after a fetch. If you'd like to continue using "reset", pass {reset: true}.
* Your route handlers will now receive their URL parameters pre-decoded.
* Added listenToOnce as the analogue of once.
* Added the findWhere method to Collections, similar to where.
* Added the keys, values, pairs, invert, pick, and omit Underscore.js methods to Backbone Models.
* The routes in a Router's route map may now be function literals, instead of references to methods, if you like.
* url and urlRoot properties may now be passed as options when instantiating a new Model.
Congratulations to Jeremy and everyone else who made this milestone possible. We've come a long way in the JS community over the last few years since SproutCore & Cappuccino were all the rage - Backbone has undeniably been a huge part of the progress we've seen.
To expand on your helpful paste, for the detail-oriented among you, the full list of changes and diff is available here: https://github.com/documentcloud/backbone/compare/0.9.10...1...
Backbone is great because it is minimal , you can start small then grow with it , like sinatra ,etc .... i like that approach.
A year ago we started our second project, and it made a lot more sense. It's a pretty big project, we've got ~10.000 LOC of Javascript, most of which is Backbone views/models/routers, some Handlebars helpers. The project has, based on our filenaming conventions, 18 routers, 68 views, and probably around 75+ models. Using Require.js we keep the codebase clean and organized, and using some custom functions (a close function, most importantly) we stop it from being a memory hog.
I like Backbone. The major competitor is Angular, but it's more high-level; Backbone itself is just a thin layer on top of vanilla JS, jquery and underscore, really, but that's what gives it its power.
Looking forward to upgrading to 1.0. The change in reset behavior will cost us some work, but it sounds like it's doable.
Or would it be a hybrid of Backbone + Angular?
Would love to hear the opinion of someone more experienced.
Regarding Backbone + Angular in a large project, I wouldn't touch that code base with a 10 foot stick.
I wish it was advertised more what large projects using Angular and that they are actually architected completely with Angular.
The view is written in HTML, you declare the bindings with moustache-style templates or with attributes. Some people get really hung up on this, but the declarative UI is not Turing complete, so it's not really like embedding PHP at all.
By the way, I like Backbone too, and I use Underscore everywhere. Congratulations to all involved on reaching an important milestone and creating tons of value for everybody.
Then the examples on the site are really misleading, given that they hardcode the controller name in the example views.
EDIT:
To give an example of what I object to, this is cut and pasted from the Angular homepage:
<div ng-controller="TodoCtrl">
<span>{{remaining()}} of {{todos.length}} remaining</span>
[ <a href="" ng-click="archive()">archive</a> ]
<ul class="unstyled">
<li ng-repeat="todo in todos">
<input type="checkbox" ng-model="todo.done">
<span class="done-{{todo.done}}">{{todo.text}}</span>
</li>
</ul>
<form ng-submit="addTodo()">
<input type="text" ng-model="todoText" size="30"
placeholder="add new todo here">
<input class="btn-primary" type="submit" value="add">
</form>
</div>
First, it hardcodes the controller name. This, to me is configuration information - part of setting up routes - that the view should not know. Then there's the ng-click and ng-submit bits. More configuration that I really don't want in my views. It also seems to declare the model to update in the view?This is what I meant about tight coupling to the views.
To answer your specific concerns, you can in fact imperatively bind Angular controllers to route views in the app's configuration block. However, this approach doesn't always make sense—you can have multiple apps in a page, each app having multiple controllers, so they can't all use the URL as the engine of application state. In cases like this you can your view as a directive, and then you can inject the appropriate controller (in fact this is exactly how ng-view works with the router).
Or, you can take the approach quoted above, and use an XML-like declarative configuration language to describe the view and its relationship to whatever happens to be its controller, and let the compiler generate the appropriate HTML for you. (I'm kidding, it's HTML all the way. But in Angular, the view describes its own structure, appearance and behavior, the controller is concerned with mutating state and not DOM manipulation.)
I'm not trying to evangelize, just put some good information out there. Backbone's templates, as I recall, use ERB-style syntax to push values and imperative code into templates, which then set up their models (http://backbonejs.org/#FAQ-bootstrap). I think you agree it would be unfair to put down Backbone as the new PHP based on that fact that its templates let you write in that style.
Enjoy!
Although I would like to see reactive fields built in; that said I've written an extension to Backbone.Model which gives me that (just ugly syntax)
Does
Renamed Collection's "update" to set, for parallelism with the similar model.set(), and contrast with reset. It's now the default updating mechanism after a fetch. If you'd like to continue using "reset", pass {reset: true}.
mean that now collections don't fire the 'reset' event anymore? Should we use collection.on('set', ...) instead? Because I think there are a lot of apps in the wild relying on the collection.on('reset', ...) syntax out there as this is seen in most of the tutorials.
collection.fetch({reset: true})
... and everything should continue to work as it did before. That said, this isn't the best forum for debugging help -- you'll probably have faster responses asking on IRC.I picked up Backbone a couple of years ago (around 0.6). It is balanced at the perfect level of abstraction for me. There have been a ton of frameworks coming up over the last few years but none of them beat Backbone for me.
Thanks for all your work and if there some place I can throw some $USD your way or a charity you prefer, please let us know.
and this boilerplate code: https://github.com/quartzmo/backbone-coffeescript-boilerplat...
(which I just added `*.coffee` sourceMapping to!)
fwiw, Routers used to be called Controllers.
Or does this logic belong in the Route.js file? I am clearly confused.
FYI.. rails didn't invent mvc; it was invented in the 70's for smalltalk.
That said, if it really bugs you, no worries:
Backbone.Controller = Backbone.View;
Boom.The break-through explanation I gave him is that Backbone views are DOM elements that are code-backed and adhere to OOP principles. They have constructors, properties, methods, etc. Ultimately, though, they are DOM elements. They can derive their content from templates, but they're still just code-backed DOM elements.
They sometimes behave as smart elements.. and sometimes they behave as controllers. Sometimes, depending on necessity, views get rendered and added to the DOM without templates at all (thus the nicety of tagName and attributes). Sometimes views necessitate a template because the result of the view is less dynamic, or merely appends more dynamic views and places them within placeholders in the template.
Point being.. They're not called controllers because they aren't controllers. They can behave as controllers, but... they're still not strictly controllers. The templates aren't called views because, although the UI is a result of the templates, they aren't always the sole source of the UI.
Backbone indeed lacks clear naming conventions if we are to stuff it in to an existing paradigm, but I have no need to do that - and I don't think you should try it either.
This feels a little off to me. The nice thing about Backbone vs. other frameworks is that it's not overly opinionated, you can structure your backend any way you want, so I'm not sure why Backbone has an opinion of what http methods save() uses. PUT as a default makes sense but it should be able to support POST, PATCH, GET, CHANGETHETHING or anything else, as browsers now support arbitrary method strings in XHR. Perhaps an override some where?
HTTP PUT is for in place entire entity updates. HTTP PATCH is for partial updates.
This isn't some opinion these guys came up with. It's HTTP standards.
Also if you don't like what save does. Write your own Backbone.sync and be done with it. Still super flexible.
That's not intended to be a counterpoint. In the wild, many companies don't use Rails. :)
With that said, it looks like many do already support it (from the same article you linked): Apache, nginx, Phusion Passenger, Unicorn, Thin, and WEBrick.
1. You can pass any arguments to the ajax call in the options hash passed to the various calls:
Foo = Backbone.Model.extend({url: "/bar" })
f = new Foo
f.fetch({type: "BLAH"})
leads to a "BLAH /bar" request instead of a GET.
2. You can override Backbone.sync to do an application-wide override. The basic implementation is fairly simple as a starting point and/or you can just wrap it.3. You can override the sync methods of Backbone.Model or Backbone.Collection. By default these just proxy to Backbone.sync
4. You can extend either of them and derive your own models and collections from your extended version.
5. You can replace .sync for any _specific_ model or collection object if you have specific sync needs for specific subsets of your application.
Given this, I really don't think cluttering the default code with more options is worthwhile - any other variations would be better provided for by providing an alternate implementation which can optionally proxy to the default/original for cases that doesn't need special handling.
This is part of what I like about Backbone - it is small and focused and sufficiently decoupled that replacing parts of the guts of it is simple to do.
> PUT as a default makes sense but it should be able to support
> POST, PATCH, GET, CHANGETHETHING or anything else, as browsers
> now support arbitrary method strings in XHR.
... it does (as mentioned by vidarh). The full suite of jQuery options are passed through transparently. What this particular feature means is that: model.save()
// PUT all of the models current attributes to model.url
model.save(attrs, {patch: true})
// PATCH just the `attrs` to model.url // Override Backbone.sync to use the PUT HTTP method for PATCH requests
// when doing Model#save({...}, { patch: true });
var originalSync = Backbone.sync;
Backbone.sync = function(method, model, options) {
if (method === 'patch') options.type = 'PUT';
return originalSync(method, model, options);
};But that said, the nature of a "breaking change" in Backbone is much less severe than in most open-source libraries. All released versions are published on GitHub, and you can use any version you see fit at any time. In addition, because the codebase is so compact, broken into atomic functions, and thoroughly commented -- it's really easy to patch to suit your own needs, should you find something you need to change without waiting for us to push a new release.
I haven't heard of anyone having a problem yet with a feature that was changed, where one of the above approaches didn't work out.
New features/improvements provide an incentive to not choose Option A. With regard to Option B: Although Backbone may be compact and easy to comprehend, the code calling it is much larger. Analyzing Backbone's changes and factoring in all code that calls it is a difficult and error prone process - laregly due to the dynamic nature of the language and lack of tooling for refactoring.
Two changes that bit us from 0.9.2->0.9.10 were model.set no longer accepting a model and, instead, requiring model.attributes (not a big deal, as it threw an exception and was immediately noticeable). The second issue, with collection.fetch, was a silent failure (I forget the exact issue, but the solution was providing two options, perhaps reset/update/add). Neither of these were documented in the changelogs.
It's not so much an issue of patching these breaking changes as it is knowing about them. Perhaps the ultimate issue here is our lack of adequate testing.
Ember was Amber for about 2 days but changed due to a naming collision with another project. I really read too much Hacker News.
Congrats and thanks to Mr Ashkenas.