taming that client side mess with backbone.js
TRANSCRIPT
Taming that client side mess with…
About me
[email protected] @jarodf
Backbone.js
Oct 2010, DocumentCloud (Underscore.js, CoffeeScript)
What does it do?
“Backbone.js gives structure to web 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 API over a RESTful JSON interface.”
Who uses backbone?
But wait, this is way less code?
$(function () { var count = 0; $('#thebutton').click(function () { count++; $("#secondview").html(count); }); });
var count = 0; $('#thebutton').click(function () { count++; if (count > 1) { $.ajax({ url: '/foo/dizzle/', type: "POST", dataType: 'json', data: someObjects.toJSON(), success: function (result) { if (result.status == 'Success') {
var html = ''; for (d in result.data) { if (d.whatever === ‘yola') { html += "<li class='yep'>" + d.name + "</li>"; } else { html += "<li class='nope'>" + d.name + "</li>"; } }
$("#secondview").html(html); toggleHistItems(result.tabId);
} else { alert(result.status); }
}, error: function (err) { uhOh(err); }
}); } else { yipKiYay(); } });
var count = 0; $('#thebutton').click(function () { count++; if (count > 1) { $.ajax({ url: '/foo/dizzle/', type: "POST", dataType: 'json', data: someObjects.toJSON(), success: function (result) { if (result.status == 'Success') {
var html = ''; for (d in result.data) { if (d.whatever === ‘yola') { html += "<li class='yep'>" + d.name + "</li>"; } else { html += "<li class='nope'>" + d.name + "</li>"; } }
$("#secondview").html(html); toggleHistItems(result.tabId);
} else { alert(result.status); }
}, error: function (err) { uhOh(err); }
}); } else { yipKiYay(); } });
The jQuery DivideRebecca Murphy – JSConf.eu
http://www.flickr.com/photos/ppix/2305078608/
Problem Not Unique
• Avoid callback soup• Patterns > Spaghetti• Logic in expected place, not tied to DOM
Model
• Thing• Business Logic• Persistence
var Todo = Backbone.Model.extend({ defaults: { content: "empty todo...", done: false }, initialize: function() { if (!this.get("content")) { this.set({"content": this.defaults.content}); } }, toggle: function() { this.save({done: !this.get("done")}); },
clear: function() { this.destroy(); } });
Common Model Functions/Properties
• defaults, initialize• get/set/unset• id/cid• custom functions• validate/isValid• save/fetch/destroy• url/urlRoot
Views
• Convention tied to a box on the page• Handles presentation logic (Presenter?)• Tied to a Model or Collections• Listens for Model changes• Listens for DOM events (click, hover, etc)
var TodoView = Backbone.View.extend({ tagName: "li", template: _.template($('#item-template').html()),
events: { "click .check": "toggleDone", "dblclick label.todo-content": "edit", "click span.todo-destroy": "clear", "keypress .todo-input": "updateOnEnter", "blur .todo-input": "close" },
initialize: function () { this.model.bind('change', this.render, this);
this.model.bind('destroy', this.remove, this); },
render: function () { $el.html(this.template(this.model.toJSON())); this.input = this.$('.todo-input'); return this; },
edit: function () { $el.addClass("editing"); this.input.focus(); }, … snipped .. });
Templates
• The ‘Markup’• Decouples UI Definition from data logic• Promotes reuse• Increased maintainability• Backbone doesn’t care which one you use– Underscore, Mustache, jsRender, jQuery etc
$.each(messages.reverse(), function(index, message) { $('#messageList').append( '<li><span class="list-title">' + message.userName + '</span>' + '<abbr class="list-timestamp" title="' + message.datePosted + '"></abbr>' + '<p class="list-text">' + message.messageText + '</p></li>'); }});
<script id="categoryTemplate" type="text/x-jquery-tmpl"> <button class="back-btn pointer" title="Back"></button> <div class="title"> ${name} </div> <ul> {{each(i, item) items}} <li class="hover" iid=${item.id}> <div class="title">${item.name}</div> <div class="desc">${item.description}</div> </li> {{/each}} </ul></script>
var fragment = $('#categoryTemplate').tmpl(category); $(this.el).html(fragment);
Collections
• Set of Models• add, remove, refresh• get, at, length• fetch• sorting• _
TV Timeout for Underscore.js
• Collections– each, any, all, find– filter/select, map, reduce
• Arrays– first, last– union, intersect
• Functions– bind, bindAll– memoize, defer
• Objects– keys, values– extend, clone
findCategory: function(id) { return _(this.categories()).find(function(cat){ return cat.id == id; }); },
var TodoList = Backbone.Collection.extend({ model: Todo, localStorage: new Store("todos-backbone"),
done: function () { return this.filter(function (todo) {
return todo.get('done'); });
},
remaining: function () { return this.without.apply(this, this.done()); },
nextOrder: function () { if (!this.length) return 1; return this.last().get('order') + 1; },
comparator: function (todo) { return todo.get('order'); } });
var accounts = new Backbone.Collection;accounts.url = '/accounts';
accounts.fetch();
Routers
• Page Routing• Control Flow• Stateful, Bookmarkable
TestRouter = Backbone.Router.extend({ routes: { "": "home", "foo": "foo", "bar/:name": "somefunction" },
home: function () {
},
foo: function () { alert('hi from foo'); },
somefunction: function (name) { alert(name); } });
Events
var object = {};
_.extend(object, Backbone.Events);
object.on("alert", function(msg) { alert("Triggered " + msg);});
object.trigger("alert", "an event");
Other
• History– a global router (per frame) to
handle hashchange events or pushState– matchs the appropriate route, and trigger callbacks
• Backbone.sync– method: the CRUD method– model: the model to be saved (or collection to be
read)– options: success and error callbacks, and all other
jQuery request options
Catalog of Events• "add" (model, collection) — when a model is added to a collection.• "remove" (model, collection) — when a model is removed from a collection.• "reset" (collection) — when the collection's entire contents have been replaced.• "change" (model, options) — when a model's attributes have changed.• "change:[attribute]" (model, value, options) — when a specific attribute has
been updated.• "destroy" (model, collection) — when a model is destroyed.• "sync" (model, collection) — triggers whenever a model has been successfully
synced to the server.• "error" (model, collection) — when a model's validation fails, or a save call fails
on the server.• "route:[name]" (router) — when one of a router's routes has matched.• "all" — this special event fires for any triggered event, passing the event name
as the first argument.
Advanced
• CoffeeScript• AMD/Require.js
Get Started
Github sitehttp://documentcloud.github.com/backbone/
Backbone Fundamentalshttps://github.com/addyosmani/backbone-fundamentals
TodoMVChttp://addyosmani.github.com/todomvc/