2. What is it?
• Small JavaScript library to organise your UI and
data on the client
• Follows the MVC pattern
• Doesn't provide widgets or HTML structure
3. Why use it?
• Libraries like jQuery don't help with code
structure
• Doesn't assume anything about your UI
• Flexible with regards to data persistence
• You can use an HTML templating engine of your
choice
• Only hard dependency is Underscore.js
• Only 3.9kB minified
4. What's in it?
• Controller
• View
• Model
• Collection
• Hash change and history handling
5. Controller
class AppController extends Backbone.Controller
routes: handler method
"users": "users"
URL hash "users/:cid/edit": "edit"
fragment parameter part
users: ->
@view = new UserListView
edit: (cid) ->
@view = new EditUserView { model: Users.getByCid(cid) }
$ ->
window.app = new AppController()
Backbone.history.start()
6. View
class UsersView extends Backbone.View
tagName: "div" the view's DOM element parameters
id: "users"
events: DOM event handler
"click .add_user": "click_add_user"
event
type selector
constructor(@users): ->
@template = _.template("HTML string with params to interpolate")
@users.bind "add", @add_user
Bind collection event to view method
@users.bind "refresh", @render
render: ->
$(@el).html @template(@users)
click_add_user: ->
@users.add() Triggers a call to add_user
add_user: (user) =>
view = new UserView {model: user}
@el.append view.render()
7. Templates
• Since Underscore.js is included, you can use
_.template which provides variable interpolation
and arbitrary JavaScript execution
• You can use a different templating engine such
as Mustache.js, Haml-js or Eco
• You could even insert elements into the DOM
instead of using templates
8. Managing templates
• One option is to use Jammit (documentcloud.github.com/jammit)
• JavaScript templates are stored alongside Rails views:
// app/views/users/user.jst
<div class = "user">
<h2>Name: <%= name %></h2>
<em>Hometown: <%= birthplace %></em>
</div>
• Jammit allows different templating engines
• Jammit concatenates, compresses and precompiles templates
• Then you can access a template on the client side like this:
JST[“users/user”]({name: “Joe”, birthplace: “Picton”})
9. Models and collections
class User extends Backbone.Model
is_invited: ->
@get("invitation_token") != null
get() and set() provide access to the attribute hash
class UserCollection extends Backbone.Collection
model: User
url: "/users"
users = new UserCollection()
users.fetch() # loads models from the server
users.add [{name: "John"}, {name: "Mary"}] Adds 2 User instances to the
collection because model is
is_invited = users.at(0).is_invited()
defined in UserCollection
10. Persistence
• Backbone.sync() is called every time Backbone reads or saves a model
to the server
• The default is RESTful JSON requests using $.ajax():
create → POST /collection
read → GET /collection[/id]
update → PUT /collection/id
delete → DELETE /collection/id
• Responding to a “read” request: send down an array of model attribute
objects
• Responding to a “create” or “update”: send the attributes of the model
changed by the server
• You can override sync() to use XML or LocalStorage
11. Persistence & Rails
• With JSON persistence, your Rails update method might be:
def update
user = User.find params[:id]
user.update_attributes params
render json: user
end
• Set Backbone.emulateHTTP = true to replace PUT and
DELETE requests with a POST + _method parameter
• Set config.active_record.include_root_in_json = false
or override parse() in Backbone models (see
gist.github.com/957474 for an example)
12. Initialising data
• One option is to include the data when rendering the Rails view:
/ index.haml
:javascript Supplied by a Rails controller
this.users.refresh(#{@users.to_json(only: User::JSON_FIELDS)});
this.checklists.refresh(#{@checklists.to_json});
This just sets collection data from the argument
#content
• The other option is to get the data on page load:
:javascript
$(function() {
this.users.fetch(); This performs an AJAX request
this.checklists.fetch();
});
• Your UI will need to handle the delay in loading the data
• You probably want to preload just enough data to display the initial page, and load the rest
using .fetch() as needed