A brief introduction to Backbone
Backbone.js is about 5kb of awesome that will give your application a basic structure and set of tools that allow you to build client side MVC applications that pulls data from RESTful JSON API’s and then displays and modifies that data based on user events. This is a basic introduction to some core concepts that will help you get started with Backbone.
Quick Overview of CoffeeScript
A few people asked for a quick overview of CoffeeScript so they can follow along so I will go over a few brief things about it to make things clear. If you’re familiar with CoffeeScript, feel free to skip this.
In CoffeeScript we use () ->
to define functions instead of function()
and the ()
is optional if you have arguments. CoffeeScript also returns the last evaluated value in a function automatically.
add = (num1, num2) ->
num1 + num2
add(1, 2)
# returns 3
Parentheses are also optional when calling methods that have arguments but are required when they have no arguments. Defining objects in CoffeeScript also has a bit of syntactic sugar by using indentation to define objects YAML style rather than requiring curly brackets each time.
x = {foo: 'bar'}
x =
foo: 'bar'
The above code is the exact same thing but with a different syntax. The only other notable Coffeescript change that I’ll cover is a very quick one. A shortcut for this
is the @
symbol so you can access properties or functions like @foo
. If you want to learn more about CoffeeScript and what it can do check out the CoffeeScript site where they provide examples on how to use it.
Backbone Models
Models in Backbone are singular objects that represent an entity that will almost always be stored in a database. Models in Backbone will be composed of the data that it is given, validations, and computed properties. Let’s build a model so we can learn about how they work.
Project = Backbone.Model.extend
initialize: ->
console.log 'a project has be instantiated'
defaults:
name: 'Default name'
description: 'Default project description'
In the code above we define a new model called Project
by extending the Backbone.Model
object and adding our own instance variables and functions. We can now create instances of Project and they will output to the console that it has been instantiated and will have the default attributes that we provided in the defaults
object. To expand upon this we’ll create an instance of project, set some attributes and get some attributes.
project = new Project {name: "Test Project"}
# creates a new project with a name of Test Project
project.get 'name'
# returns 'Test Project'
project.get 'description'
# returns 'Default project description'
project.set 'description', 'A test project to show off models'
project.get 'description'
# returns 'A test project to show off models'
project.save()
# will send a post request with the model attributes as parameters
Here we’re just creating a new project, setting the name and description in two different ways and showing you how to get attributes from the model. These attributes are stored in the models attributes
array. We use set instead of modifying the attributes array directly so we can get change events on our model and we use get so we don’t accidentally change data when all we really want to do is read from it.
One quick note, even if you don’t want a change
event to trigger, don’t modify the attributes array directly, just pass silent: true
to the options object when you set the attributes value.
If you want to get more into the details of models read the documentation on Backbone.Model on the Backbone.js website.
Backbone Collections
A simple way to think of collections in Backbone is that they’re just multiple models grouped together into a collection, hence the name. You can create a collection the same way that you created our Project
model class in the earlier example.
Projects = Backbone.Collection.extend
model: Project
That’s it! That’s all that is required to define a collection in Backbone. You just specify the project so it knows how to parse the data that you pass to it. Collections will play a vital role in our app and just about every Backbone app. Next I’ll go over a few very important methods that you can use on collections but again won’t go into detail or cover all of the methods available.
# Note: This is usually defined when you extend Backbone.Collection
Projects.comparator = (project) ->
project.get "id"
Here we’re creating a comparator for our collection. This will allow our collection to maintain the order that you define and in this example is by our models id. This will not resort if we changed a models id that was inside of the collection. To resort the collection you must call the sort
function. If you want more detail on this, check out the documentation on comparator.
Project = Backbone.Model.extend
url: ->
"/projects/#{@get 'id'}"
Projects = Backbone.Collection.extend
model: Project
url: '/projects' # Note: This can be a function or a variable
projects = new Projects()
projects.fetch()
# sends a get request to the defined url and turns the received data into models
projects.models
# returns an array of the models the collection contains
projects.filter (project) ->
project.get('id') > 3
# returns an array of models where the function returns true
project = new Project {id: 7}
# create a new project and specify an id
project.fetch()
# send a get request to the server to retrieve the model that our url function returns
projects.add project
# add the project to the collection
In the above code we do quite a bit, but it’s really only 3 core concepts. First, we redefine Project
and Projects
so we can have url’s and pull data from our non-existent server. The url value in models and collections can either be a string, or a function and you will almost always have the value of url for a model as a function so that you can do things such as specify the ID of itself or use the base path so that you can create a new model on the server. Next we create a new instance of our Projects
collection and call the fetch
function to send a get request to the server which will return a JSON representation of our models so it can parse them and add them to itself.
Next I show off two different ways to access the underlying models. Both of these are commonly used in our templates so that we can loop over them and display them to the user. The models
method is very self explanatory and I think filter
is as well, but I’ll go over it a bit. Basically you pass it a function that takes a single argument which is a model in that collection. If you want to know more, read about filter on the underscore.js documentation site.
My last example is a bit more realistic. First we’re going to create a new instance of our project model and pass it an id
attribute of 7. We pass this model an ID so we can call the fetch
method on it and it will send a get request to the server for that particular model. We then take that project and add it to the collection which is again super simple. You should start to see a pattern here. All of the examples are very similar and build on top of one another and that’s what Backbone was designed to do.
Backbone Views
When referring to views Rails and Backbone have two totally different ideas for them. In Rails a view is actually a template and not really a view in the Backbone sense. To simplify things a view in Backbone is similar to Rails controllers.
Views in Backbone manage individual parts of your application and have several roles. First, they are used to render content onto your page usually using templates (we’ll get into templates in the next section). Most importantly views will handle events that you define in an events object inside of your view.
ProjectIndex = Backbone.View.extend
template: JST['projects/index']
initialize: ->
@collection = new Projects()
@collection.fetch()
# this isn't usually done
@collection.bind 'reset', @render, this
# re-render view when collection resets
render: ->
$(@el).html @template(projects: @collection)
this
So far this is our most complicated example and we’re actually using a collection with our view. First we extend our Backbone.View as usual and then we define our template. JST requires a bit of explanation because it’s special in a Rails context. JST is provided by Sprockets and compiles files with a .jst.eco
or .jst.ejs
template in your app/assets/templates/
directory and turns those templates into functions for you. JST['projects/index']
is actually a function but we don’t call it immediately so we can pass it a context object later on in our render call.
In our initialize
function we’re creating a new collection and assigning it to @collection and then fetching it. Normally this would be passed when creating the view from the router new ProjectIndex(collection: projects)
. If you pass a view model
or collection
when creating it Backbone will automatically assign them to @model
or @collection
respectively. The last line in our initialize function is very common and important to have. This says whenever the collection receives a reset
event we’ll re-render our view to keep things up to date. Whenever the fetch
we called on the collection is returned it will reset the collection and re-render our view with the new data. Two more very important events for collections are the add
and remove
events which are triggered whenever models are added or removed from the collection. If you want a more comprehensive list of events that Backbone can fire, check out the catalog of events on the Backbone documentation site.
The last function in the example is the render
function. This function is extremely flexible and can be a simple render like this example, or can even be used to loop over child views to render them inside of our view. If you’re paying close attention you might be confused where @el is defined. It’s actually defined by Backbone and is the DOM element of the view whether or not it has been inserted into the DOM or not. You can define the tagName
, className
, id
, and attributes
attributes on your view and they will be applied to the element. If tagName
is not specified and you do not call setElement
on the view it will by default be a div
tag. We wrap our element and replace the contents with what our template function returns. As you can see we’re passing our collection to the template context as projects
and will be available as @projects
inside of the template. These are the basics of using Backbone views but if you want to take a deeper look into them you should check out the Backbone.View documentation.
Templates
Templates in Backbone are what Views are in Rails. Templates allow you to embed JavaScript or CoffeeScript expressions into HTML. There are quite a few options to choose from but we’ll be using Eco for our templates. Other options include EJS and Handlebars. Using Eco in a Rails application is easy. We just create the file the app/assets/templates
with an extension of .jst.eco
and we’re set. We’re going to stick with our previous example from the view section where we rendered our template into our view.
<% for project in @projects.models: %>
<div class="project">
<h1>Project ID: <%= project.get('id') %></h1>
</div>
<% end %>
Evaluates to the following HTML if the collection we passed to it only had one element with an ID of 1.
<div class="project">
<h1>Project ID: 1</h1>
</div>
There’s really not much to explain here. <% %>
evaluates expressions and <%= %>
evaluates the expression and prints the return value. There’s a few more details about Eco that aren’t covered here that you can learn about on their Github repo at https://github.com/sstephenson/eco.
Backbone Router
I saved the router for last because it requires a combination of everything we have already learned. The router is the core of your application and maps url’s to functions that will fetch models or collections as well as creating views while passing those models or collections.
router = Backbone.Router.extend
routes:
'': 'index'
index: ->
projects = new Projects()
projects.fetch()
view = new ProjectIndex(collection: projects)
$('body').html @view.render().el
This is a very minimal and simple router that contains a single route for examples sake. First we extend as usual and define a routes object in our router. This is what maps our url to a function in our router. Here we pass in a blank string which matches the root path. In our index function we’re taking everything we learned in the previous section and combining it together. First we create a new collection of projects and fetch it then pass it to a new instance of our ProjectIndex
view as the view’s collection. Finally we call the render function on our view and retrieve the el
attribute so we can insert it into the body
of our page.
Conclusion
This should cover the basic concepts of Backbone which can be built upon to structured applications. It’s also encouraged to add methods to the prototype of Backbone objects or even just make your own subclasses. If you don’t feel like reinventing the wheel you can always check out the wiki page on the Backbone.js Github repo titled “Extensions, Plugins, Resources” which as the name implies, has a list of extensions, plugins, and resources.