Movatterモバイル変換


[0]ホーム

URL:


Hi, I'm James Yu

I create things

Backbone.js Tutorial with Rails Part 2

If you like Backbone,you'll love Parse. ReadPart 3 of this tutorial where I show you how to convert the CloudEdit app to use Parse without any server-side Rails code at all

InPart 1 of the CloudEdit Backbone.js Tutorial, we developed a basic Rails application usingBackbone.js that lets users create and edit documents in the cloud. Now, in Part 2, we'll do some refactoring to clean up parts of the app and make things more readable and maintainable.

Specifically, we'll be doing the following:

  • Use Backbone Collections.
  • Use Underscore templating.
  • Use event binding to refresh views.

This update won't change anything in the UI: it's simply some housekeeping to tidy up the code.

As always, you can follow along with theCloudEdit GitHub repo, and also play with thelive app here. They both have been updated to reflect this part of the tutorial.

Backbone Collections

As you may remember in Part 1, we loaded the list of documents for thedocuments#index action via a call to$.getJSON, and then instantiated all the documents in an array. But, we can provide a better abstraction by defining a Backbone Collection as follows:

App.Collections.Documents=Backbone.Collection.extend({model:Document,url:'/documents'});

public/javascripts/collections/documents.js

It's pretty simple: we tell the collection that it should hold theDocument model (via the model attribute), and that the resource to fetch the documents from the server is located at/documents. Also notice that I'm organizing collections in the same way as the MVC components: the definition is located under theApp.Collections object.

Now, we can update thedocuments#index action in the Backbone controller as follows:

App.Controllers.Documents=Backbone.Controller.extend({// ... snip ...index:function(){vardocuments=newApp.Collections.Documents();documents.fetch({success:function(){newApp.Views.Index({collection:documents});},error:function(){newError({message:"Error loading documents."});}});},// ... snip ...});

public/javascripts/controllers/documents.js

All we did was instantiate a new instance of theDocuments collection, and then call fetch with a success callback that passes the collection to theApp.Views.Index view. We didn't even need to change any Rails code: the original RESTful/documents action is identical.

Underscore Templates

Previously, we built up our views using string concatenation. I did this so that we could focus on Backbone.js itself, and not any particular templating language.

However, for anything more than trivial views, string concatenation is a maintenance nightmare. Luckily,http://documentcloud.github.com/jammit/ provides an easy integration withhttp://documentcloud.github.com/underscore/ templates, which are powerful and very similar to ERb.

.jst Files

Jammit expects your javascript templates (or JST) to live alongside your regular ERb templates as.jst files. It will package up the templates into a globalJST object that you can use to render your templates into strings. To make Jammit aware of these files, I simply added an entry forapp/views/**/*.jst in myapp package inassets.yml.

Convert the Views

Next, we need to convert our views to Underscore templates. This is the fun part, since we get to see the ugly jumble of strings turn into beautiful templates.

Let's first convert the strings in theApp.Views.Edit view into thedocument.jst template. This would turn the following code:

varout='<form>';out+="<label for='title'>Title</label>";out+="<input name='title' type='text' />";out+="<label for='body'>Body</label>";out+="<textarea name='body'>"+(this.model.escape('body')||'')+"</textarea>";varsubmitText=this.model.isNew()?'Create':'Save';out+="<button>"+submitText+"</button>";out+="</form>";

into:

<form><labelfor='title'>Title</label><inputname='title'type='text'/><labelfor='body'>Body</label><textareaname='body'><%= model.get('body') %></textarea><button><%= model.isNew() ? 'Create' : 'Save' %></button></form>

app/views/documents/document.jst

If you're familiar with ERb templates, this is pretty straightforward. Basically, the template now uses themodel object that is passed in to fill in all the data. The call to render this template is:

$(this.el).html(JST.document({model:this.model}));

No more complicated string concatenation!

Now let's convert the strings inApp.Views.Index into thedocuments_collection.jst template. This turns:

if(this.collection.models.length>0){varout="<h3><a href='#new'>Create New</a></h3><ul>";this.collection.each(function(item){out+="<li><a href='#documents/"+item.id+"'>"+item.escape('title')+"</a></li>";});out+="</ul>";}else{out="<h3>No documents! <a href='#new'>Create one</a></h3>";}

into:

<% if(collection.models.length > 0) { %><h3><ahref='#new'>Create New</a></h3><ul><% collection.each(function(item) { %><li><ahref='#documents/<%= item.id %>'><%= item.escape('title') %></a></li><% }); %></ul><% } else { %><h3>No documents!<ahref='#new'>Create one</a></h3><% } %>

app/views/documents/documents_collection.jst

Similar to thedocument.jst template, this template derives all its data from thecollection object that is passed in. We would render it like:

$(this.el).html(JST.documents_collection({collection:this.collection}));

If you take a look at theApp.Views.Edit andApp.Views.Index models, they are now significantly simpler after moving the HTML out.

Model Event Binding

One last minor cleanup that we'll do is to avoid callingrender in thesave method ofApp.Views.Edit. Instead, we'll bind therender call to any model changes, like so:

App.Views.Edit=Backbone.View.extend({// ... snip ...initialize:function(){_.bindAll(this,'render');this.model.bind('change',this.render);this.render();},// ... snip ...});

Now, whenever thedocument model changes, the view will be re-rendered. This ensures that the view will always stay up-to-date with the model, no matter what piece of code happens to change it. This is actually fundamental to the philosophy of Backbone, which is to separate the model data from the controllers and views.

Conclusion

Let's take a look at the updated directory structure after these changes:

app/    controllers/        documents_controller.rb    models/        document.rb    views/        home/            index.html.erb        documents/            document.jst            documents_collection.jstpublic/    javascripts/        application.js        collections/            documents.js        controllers/            documents.js        models/            document.js        views/            show.js            index.js            notice.js

We added two items: the .jst files, and the Backbone collections folder. Overall, the structure is still nicely organized, and its easy to see at a glance how everything connects.

After this update, we have a robust base that we can powerfully extend with more features. What you'll discover is that with Backbone, you avoid a lot of churn that is usually present in persisting data and view fragments in a javascript heavy Rails application. There is now a logical place for all client-side code.

In Part 3, learn about how to convert CloudEdit from Rails to Parse, so you don't need to do any server side coding at all!

Posted on09 Feb 2011
Tweet

James Yu is theco-founder ofParse, lives in San Francisco, and likes toaccidentally the whole stack.

Archives

13 Jun 2013Staring at Zero
29 Nov 2012The importance of startup momentum
23 Nov 2012Do everything. Then hire.
18 Nov 2012Startups should think big and start small
20 May 2012Converting CloudEdit from Backbone to Parse in 5 minutes
13 Jan 2012Double Down
06 Jan 2012Hone the Core of Your Product
31 Dec 2011Things I Learned Building a Company in 2011
10 Mar 2011On Being an Early Startup Employee, and a Farewell
16 Feb 2011Progressive Signup
09 Feb 2011Backbone.js Tutorial with Rails Part 2
08 Feb 2011Empathetic Product Discovery for Hackers
05 Feb 2011Introducing Gmailr: A Javascript API for Gmail
27 Jan 2011CloudEdit: A Backbone.js Tutorial with Rails (Part 1)
27 Jan 2011Zodiac Hacking: An Accidental SEO Experiment
26 Jan 2011My New Jekyll Blog
09 Oct 2010How My Gap Logo App Became Viral
31 May 2010Tips for the Digital Traveler in Italy
31 May 2010Eating in Italy
15 Apr 2009Foursquare is my Location Memory
11 Jan 2009Why Table Tennis is a Great Hacker Sport
14 Dec 2008A Guide for Your First Usability Test

[8]ページ先頭

©2009-2025 Movatter.jp