Using Backbone.js with Rails

92497f51734ed56687928d5fd68d870a?s=47 sarahmei
April 25, 2012

Using Backbone.js with Rails

RailsConf 2012 presentation on how and why to use backbone.js in a Rails application.

92497f51734ed56687928d5fd68d870a?s=128

sarahmei

April 25, 2012
Tweet

Transcript

  1. Backbone.js & Rails: Patterns In The Wild Sarah Mei @sarahmei

    sarah@pivotallabs.com Wednesday, April 25, 2012 I’m Sarah Mei, and I’m a Ruby and JavaScript developer at Pivotal Labs. Pivotal is a consulting company, so I’ve been on a lot of projects in two years I’ve been there. Before I came to Pivotal I could easily spend a year or two on the same project, but now my average project is about 2 months. And in the last year or so, nearly all of my projects have used Backbone. Backbone can be used in lots of different ways, it’s a ‘lightweight’ framework, which distinguishes it from Rails. Rails is ‘opinionated’ - there is a right and a wrong place to put different types of code. But because backbone is lightweight, it can be used in a lot of different ways, and across different projects, there sometimes isn’t much in common. So this talk is essentially a backbone experience report, from a Rails developer’s perspective. I think the Rails developer’s perspective is important, and not just because we’re at rails...conf. One of the least obvious parts of something like backbone is .. why do you need it? A lot of the docs for backbone - and many of the other related frameworks - approach everything from a JavaScript developer’s perspective, and assume that you already know why you need to use it. So I will de-mystify that process a little bit, and once you know the why, we’ll then look at two different ways to use it.
  2. Agenda • What is it? • JavaScript & Rails: a

    love story • What backbone gives you • Pattern 1: Greenfield • Pattern 2: Application in transition Wednesday, April 25, 2012 So here’s our agenda! There’s a lot to cover here, so I’m going to move pretty quickly. I will be publishing these slides, plus annotated source code from the rest of the talk, later this week, so don’t feel like you have to take comprehensive notes. How many people have worked on a Rails app that used backbone.js? How many people are just here to find out what the hell it is? And how many are here to find out whether your pattern of backbone usage is in any way similar to mine?
  3. What is it? MVC X Wednesday, April 25, 2012

  4. Photo credit: http://www.flickr.com/photos/knott/21665911 Wednesday, April 25, 2012 Think of it

    as a set of cubbies in which you can put different types of code, to keep them organized. Unfortunately for us, some of the cubbies are named similarly in backbone and in rails, which is confusing. So let’s break it down.
  5. Available cubbies • Models • Templates • Views Backbone.js: •

    Models • Views • Controllers Ruby on Rails: Wednesday, April 25, 2012 Now the first thing to know is that backbone has nothing called “controllers,” so there is no C in their MVC anyway. Instead, backbone has models, templates, and views. Now rails has models, views, and controllers. But before we dive into this comparison, let’s talk a little bit about why you’d want to do this. Why marry these two frameworks at all?
  6. Photo credit: http://www.flickr.com/photos/gotosira/4699302559 My JavaScript Is Fine Just The Way

    It Is Wednesday, April 25, 2012 The basic idea is better organization. But I want to unpack that a little bit and talk about how we got to the point where we need a framework to sort this all out. What’s wrong with plain old JavaScript?
  7. Agenda • What is it? • JavaScript & Rails: a

    love story • What backbone gives you • Pattern 1: Greenfield • Pattern 2: Application in transition Wednesday, April 25, 2012
  8. JS & Rails: a love story Phase 1: JS X

    Wednesday, April 25, 2012 So I want to talk about the evolution of the relationship of JavaScript and Rails. And in some ways this also the evolution of my relationship with JavaScript and Rails, and I think a lot of developers go through these same phases. The first phase is the no-JavaScript phase. This is the phase in which you use the JavaScript helpers that Rails gives you. And for me, when I first came to Rails in 2006, I came from J2EE land. So I was learning Ruby, I was learning Rails, and to make it worse, I had been a “backend” engineer, which meant I knew next to nothing about JavaScript or CSS. And all those JavaScript helpers really seemed amazing and magical. I could write Ruby, and I’d get JavaScript. I mean...you can’t explain that. So let’s look at an example...
  9. button_to  "Create",      :action  =>  "create",      :remote

     =>  true <form  method="post"              action="/images/create"            class="button_to"              data-­‐remote="true">  <input  value="Create"  type="submit"/> </form> Wednesday, April 25, 2012 So if you do this in your view, you get a piece of rendered HTML that looks like this. Now this is the nice unobtrusive Rails 3 version, so the key is here: the data-remote=‘true’ attribute. When you include rails.js, it attaches itself to the submit event of any form with that attribute, and submits it ajaxily instead of doing a page refresh. Kinda cool. Kinda magic. Here’s another example...
  10. link_to_remote  "Add  to  cart",    :url  =>  add_to_cart_url(product.id),    :update

     =>  "cart" Wednesday, April 25, 2012 Now this one really is magic. If you put this in your rails view, it magics up a whole form which is too big to fit on the slide, but what it does is make a link that when you click it, it goes to the URL you specify, which in this case is add_to_cart_url, and then it updates a piece of DOM with whatever comes back. In this case it updates the element with ID ‘cart’, so it will take the rendered HTML that is returned from the add to cart action, and put it into the cart div. And so this kind of thing is certainly fine for simple interactions. For a lot of apps, this is fine. But for me, I tend to think of these things as the JavaScript equivalent of scaffolding. Good for whipping something up fast at a hack night, but not something you use in real applications. We’re at the point now where JavaScript needs to be a first-class citizen in our web applications. And why are we at that point?
  11. Image credit: http://i.chzbgr.com/completestore/12/4/22/A3M8QMvalEiXdwuJP5U4xA2.jpg Wednesday, April 25, 2012 Well, it’s mostly

    because of that whole “web 2.0” thing. Besides shortening company names, in a practical sense, what web 2.0 really was, was the product people starting to request more interactivity than the Rails JavaScript helpers can easily deliver. What if you have a request and you want to update two different divs with different content? You can force the helpers to do that, but not without significant effort, and you do have to start writing actual JavaScript. And at that point it’s actually easier to stop using the helpers and start using jQuery directly. Which brings us to phase 2...
  12. JQuery explosion Phase 2: JS & Rails: a love story

    Wednesday, April 25, 2012 ...the jQuery explosion. This is what happens when you convert your Rails helper calls to actual JavaScript. And it’s called the explosion because you start getting stuff like this at the top of each of your views:
  13. -­‐  javascript_tag $(function()  {    $(‘.create-­‐form’).submit(        function()

     {              $.ajax(...).done(              function(data)  {                $(‘.yay’).html(‘good  jorb!’);                $(‘.count’).html(data[count]);              });        });    $(‘.username’).autocomplete(); }); Wednesday, April 25, 2012 As you can see we’re calling a jQuery plugin down there at the bottom; your JavaScript quickly gets littered with those. But here at the top, you’re doing some pretty standard ajax stuff. You can imagine, though, that this pretty quickly gets unwieldy. In fact with a bunch of ajax at the top of every view, even your views, which are supposed to be dumb and have no logic, start looking messy.
  14. Photo credit: http://www.flickr.com/photos/puyo/253932597 Wednesday, April 25, 2012 And a lot

    of developers, and a lot of rails apps, stop at this phase. They may pull the js out into a file and include it in their views but it’s still messy. It’s hard to test - integration tests are the only way to get at it, and those are sloooow. So as the interaction quotient goes up, the tests get slooower. The other option is not to test it, which I know a lot of people opt for. I think I’d be fired if I suggested that that was even an option. So a lot of people looked at this problem and many came up with the same solution...
  15. Page Objects Phase 3: JS & Rails: a love story

    Wednesday, April 25, 2012 ...Page objects. In this pattern, we take all that JavaScript that was in the view, or thrown into a file included in that view, and we separate it into unit-testable functions in a JavaScript object that is scoped to the page being rendered. Let’s look at an example...
  16. MyApp.ImagesNew  =  {    initialize:  function()  {      

     $(‘.create-­‐form’).submit(              submitCreateForm        );        $(‘.username’).autocomplete();            },    submitCreateForm:  function()  {        ...    }     }; Wednesday, April 25, 2012 Here’s the page object that contains the JavaScript that we previously had thrown into the view. It has an initialize function, and we’ve pulled out the anonymous function that we had been passing to click, and given it a name.
  17. MyApp.ImagesNew  =  {  initialize:  function()  {      MyApp.ImageCreateForm.initialize();  

             MyApp.CartSummary.initialize();      MyApp.SearchBox.initialize();      MyApp.RelatedProducts.initialize();    }     }; Wednesday, April 25, 2012 And typically, you’ll start to break your pages down into components, or widgets, and a page class is the thing that calls initialize on each of them in document.ready. So that JavaScript that you had thrown into the view goes...
  18. -­‐  javascript_tag $(function()  {    $(‘.create-­‐form’).submit(        function()

     {              $.ajax(...).done(              function(data)  {                $(‘.yay’).html(‘good  jorb!’);                $(‘.count’).html(data[count]);              });        });    $(‘.username’).autocomplete(); }); Wednesday, April 25, 2012 ...from this...
  19. -­‐  javascript_tag  do    $(function()  {        MyApp.ImagesNew.initialize();

       }); Wednesday, April 25, 2012 ....to this. And this is much, much nicer. You can have an integration test that just tests that you’ve called initialize, and then you can unit test the functions in MyApp.ImagesNew - the page class - to make sure those are working the way you expect. This pattern can take you pretty far. There are even gems out there that do this part of the code automatically, guessing the name of the page class based on the controller name and action name you’re calling in Rails, and inserting JavaScript code that calls initialize on document-ready into your view without you having to do anything. Pivotal had one called jelly, back in the day. There *are* Rails apps out there for which this pattern is sufficient. It starts to break down when your product people, again wanting to keep up with your competitors, start requesting more page interactivity than you can reasonably do with just ajax and redirects. For example, when you add something to the cart, they start wanting to re-render the entire cart area on the nav bar - a decently large chunk of markup - plus they want to slide a ‘related products’ sidebar into view, to get some upsell action. Slightly more complicated than what they wanted before. Typically at this point, you start considering ...
  20. Image credit: http://www.flickr.com/photos/whatsthatpicture/6368412285 Wednesday, April 25, 2012 ...doing a little

    client-side view rendering with something like mustache or handlebars. Just a little bit at first - maybe a notification number update, or the related products sidebar. But really, really quickly, page classes break down, because you start trying to attach templates to page classes, or to their subcomponents, and you need to start knowing when you should re-render the templates, which means, usually, that you need to do more than just call a function on document-ready and forget it, which is essentially what you’re doing with page classes. You might need to, oh, I don’t know, maintain some state. So the general rule here is that page classes are awesome, and probably sufficient to organize and test your JavaScript, *if* you’re still doing server-side view rendering, using ERB, or HAML, or something like that.
  21. Image credit: http://www.flickr.com/photos/quasimondo/3892920130 Wednesday, April 25, 2012 If you’re starting

    to do client-side view rendering then you’re probably also starting to run into the tyranny of events. So you start generating events inside the sub-components of your page class. This happens when you have one component on the page with DOM events that affect other components on the page. For example, our add to cart button that needs to notify the nav bar and the sidebar that they need to change. It’s not coincidental that these happen together. More interactivity, more fluidity, more speed, all tend to lead you in this direction, and the product people sure as hell want to go this direction. The events are particularly messy, though, because jQuery events only bubble up. So if you have a hierarchy in which you have a page class with a number of sub-components, and one sub-component generates an event that another sub-component needs to consume, it’s up to the parent object - in this case, the page object - to bind to that event from sub- component A, and when it gets one, notify sub-component B. So the page class starts having to know all about its sub-components’ business. You start wishing for a top-level event handling system.
  22. Photo credit: http://www.flickr.com/photos/kirkt/127384374 Wednesday, April 25, 2012 Usually around the

    same time, you also start wishing there were some way to mirror your model objects between the server and the client. For example, your product objects, or your cart objects. Because it starts feeling like you’re writing the same REST-ful ajax methods to create the cart and show the cart and update the cart and create a product and show a product and update a product...every single time. So it’s increased interactivity, and its concomitant drivers of client-side rendering, events, and model mirroring, that drive Rails developers into...
  23. Frame works Phase 4: JS & Rails: a love story

    Wednesday, April 25, 2012 ...the arms of frameworks. Now, there are a lot of frameworks out there, which all solve these problems in different ways.
  24. Backbone.js Cappucino Ext.js Angular.js Knockback.js Agility.js Sammy.js Knockout Spine.js JavaScriptMVC

    Ember.js Sproutcore Google Web Toolkit Batman.js Google Closure Meteor Wednesday, April 25, 2012 There’s backbone, and then there are others. There are probably others that aren’t on here, since I made this slide yesterday. It’s good, though, it’s a reflection of the fact that we all as a community hit this JavaScript wall at about the same time.
  25. Agenda • What is it? • JavaScript & Rails: a

    love story • What backbone gives you • Pattern 1: Greenfield • Pattern 2: Application in transition Wednesday, April 25, 2012 So let’s go back to what backbone gives you.
  26. What Backbone gives you • Model mirroring (Models & Collections)

    • Views that handle events (Views) • & maybe render DOM (Templates) Wednesday, April 25, 2012 It gives you semi-automatic model mirroring, and we’ll come back to what means in a little bit. That’s handled in what backbone calls models and collections. It gives you views that handle DOM events, and they can also, optionally, be attached to a template that renders markup.
  27. Available cubbies • Models/Collections • Templates • Views Backbone.js: •

    Models • Views • Controllers Ruby on Rails: Wednesday, April 25, 2012 So I want to come back to this model-template-view-controller comparison chart. As I mentioned just now backbone.js gives you model mirroring using objects it calls models and collections. And actually, the models are actually about the same on both sides. They’re meant to encapsulate a business object of some kind, and on most of my backbone/rails projects, our backbone models have been a subset of our rails models, with the same names. And a Collection, in backbone land, corresponds to an array or more broadly a set of models of the same type. Now here’s where the terminology starts to get confusing. “Views” in rails correspond to “templates” in backbone. In backbone, the template is the logic-less interpolated HTML. Note that there is no arrow between “views” on the rails side, and “views” on the backbone side. So if views in backbone aren’t templates...what are they? And the answer is, they are basically a piece of the controller. Views in backbone are JavaScript objects that own a particular piece of the DOM, set up the instance variables for the templates, render the templates, and handle the events that occur in their region of the DOM. And backbone gives you mechanisms to help with all that stuff, that we’ll take a look at in moment. But the takeaway here is that when I say backbone view, don’t think a dumb template - think a piece of code that controls a certain region of the DOM. Anyone done any iOS or desktop GUI/cocoa/etc. programming? So for you folks, this may look a little more familiar. The backbone model is more similar to iOS and the desktop GUI models of MVC. In fact some people call backbone views “View Controllers,” which is a concept also found in iOS. So views are not templates. It seems like an obvious point, and you may wonder why I’m belaboring it. The answer to that is that none of the docs really make this explicit, and it took working with it for a while before I was able to articulate the differences. When I started working with backbone, I was hesitant to put logic in my view classes, for example, because in Rails we typically don’t. So I’m trying to spare you that learning curve. :)
  28. Wednesday, April 25, 2012 So! Let’s all take a break

    for a moment. Deeeeeep breath. Stretch...I took this picture on my in-laws’ dock in Novato, California, which is one of the most beautiful places in the world. And, you’ve probably noticed this, but that was mostly for me. You’re welcome to take a deep breath too, though. Ok! Now that we’ve covered how all these frameworks came to be, and why Rails developers might want to use one, let’s look at how actual Rails apps actually use backbone. I’m going to show you two very different patterns, but you’ll be able to see the “page object heritage” in both of them.
  29. Agenda • What is it? • JavaScript & Rails: a

    love story • What backbone gives you • Pattern 1: Greenfield • Pattern 2: Application in transition Wednesday, April 25, 2012
  30. Pattern the first Rails === API Wednesday, April 25, 2012

    This is a pattern that I see a lot in new, greenfield Rails applications. It’s essentially “Rails as an API,” and what it means is that in most new Rails apps, we do no server-side HTML rendering at all. Instead we build a server that just serves data, usually in the form of JSON, and we leave it up to the JavaScript to render all the markup that the user sees. Now, this is a crazy amount of work compared to doing a standard server-side rendering app. To be clear, most apps don’t need to do this. But at Pivotal we are seeing more companies coming in who are building a website and native mobile apps at the same time. Now those two types of clients have different API requirements, but if you have a REST-ful server returning JSON, you’re a good portion of the way there towards what mobile apps need. So if Rails is just an API, then what does your app/views folder look like?
  31. %html %head = stylesheet_link_tag 'default' = jquery_include_tag = javascript_include_tag :main,

    :templates = yield(:head) %body #content = yield Wednesday, April 25, 2012 Well, you typically still have a layout that is used in just one controller method. This puts in the HTML head and body elements, and includes all the javascript and CSS and is all asset- pipeline-y. But then the body itself is fairly simplistic: a div with ID content, and that’s it. Then you need the one controller method...
  32. layout :home before_filter :authenticate_user! def index end Wednesday, April 25,

    2012 This is the full contents of your HomeController. You tell it to use the layout that we had in the last slide, then we make sure there’s a current user object, and that’s pretty much it. It renders the index view. Let’s have a look at that.
  33. -­‐  javascript_tag    $(function()  {        myApp.initialize();  

     }); Wednesday, April 25, 2012 Now, we saw something like this when we were looking at page objects, but in that case, the JavaScript was followed by erb, or haml, or what have you, that rendered the markup. And the page object then overlaid that with interactivity. In this pattern, by way of contrast, *all* you have in your Rails views is a little bit of JavaScript that gets the whole app going.
  34. -­‐  javascript_tag    $(function()  {        myApp.initialize(  

             =  current_user.to_json        );    }); Wednesday, April 25, 2012 And sometimes, so that your app can get off the ground, you also print some data into the page. And that’s the last time your Rails app renders markup. From then on your JavaScript uses the app as an API only, requesting JSON. The Rails app has the same set of controllers it would have in a server-side-rendering app - cart controller, products controller, etc. - but instead of rendering a view and returning that as a string...
  35. def index render :json => Product.all end Wednesday, April 25,

    2012 ...they render the object you need to json and return that as a string. And to do that you just use the render method with the json key, and usually a set of decorators or overridden as_json methods on your models. Simple enough, right? So. What does it look like on the JavaScript side?
  36. Wednesday, April 25, 2012 There’s about to be a LOT

    of javascript code. So let’s go back to this place for a moment. Don’t worry if you don’t have time to figure out what everything is doing; I’ll be posting this code with more detail and comments later in the week. Watch twitter to see that. But I want to take you on a quick tour to see what the backbone code actually looks like.
  37. var myApp = { models: {}, views: {}, initialize: function(currentUserData)

    { myApp.currentUser = new myApp.models.User(currentUserData); myApp.navBar = new myApp.views.NavBar( {model: myApp.currentUser} ); } }); Wednesday, April 25, 2012 Usually you’ve got an app object, here it’s a plain old var’d JavaScript object, myApp. It’s got a set of model constructors, and a set of view constructors, and it’s got an initialize method. This method is what’s called in your Rails views, remember way back there? And what it does is two things: it creates a model instance to represent the current user, and it creates a view instance to control the navigation bar. Typically the app intialize will set up any views that are always present on the page, as well as any global state such as the current user. Pretty straightforward so far? ...maybe. Hmm. Let’s look at something a little simpler: the backbone user model.
  38. app.models.User = Backbone.Model.extend({ urlRoot : "/users" }); Wednesday, April 25,

    2012 Nice and simple to start with. Here’s our user model. Notice it has a urlRoot attribute - this is the magic that makes it so that it knows how to do a get on /users/id to get data, and a put on /users/id to update the data. You can change attributes on the user model and call save, and it will persist them up to your REST-ful API. Now where does it get the attributes to begin with? Well, backbone models have attributes, and you can just pass them in to the initialize, if you don’t define an initialize in your model, it just sets all those attributes. In our case, these attributes come from the json data that we printed into the page in our Rails view. Remember this?
  39. -­‐  javascript_tag    $(function()  {        myApp.initialize(  

             =  current_user.to_json        );    }); Wednesday, April 25, 2012 There’s our current user json, being passed into the application’s initialize...
  40. var myApp = { models: {}, views: {}, initialize: function(currentUserData)

    { myApp.currentUser = new myApp.models.User(currentUserData); myApp.navBar = new myApp.views.NavBar( {model: myApp.currentUser} ); } }); Wednesday, April 25, 2012 ...which in turn is being passed to our user model’s initialize. And THEN, we passed that model object into the nav bar view that we created. Ok! Let’s move on to the second action here, which creates a new view to render the nav bar. It passes in the currentUser model instance that we just created as the ‘model.’ Let’s have a look at the view code. Get ready, this is a little more dense than the model...
  41. myApp.views.NavBar = Backbone.View.extend({ templateName: "navBar", className: "nav-bar", events: { "click

    .profile" : "showProfileMenu", "click .main-logo" : "goHome" }, initialize : function() { this.model.bind('change', this.render); this.render(); }, render : function() { this.template = JST[this.templateName]; this.$el.html(this.template(this.model)); return this; } }); Wednesday, April 25, 2012 Ok, now, this is getting more complicated. This view object extends Backbone.View, just like our user model extended Backbone.Model. Then we’re passing it an object that has overrides for the Backbone default view code. So we name the template this view uses, we tell it what class to put on the div it creates when it renders, then we tell it about some DOM events that we care about, then we override the initialize method, and then we override the render method. Now that events object is built in to backbone views. The keys are the DOM events you care about that happen within this view, and these can be custom events, along with the selector you expect them to happen on. So click .profile means any click event on an element with class profile, within this view. Then the values of this object are strings that are names of functions to call when that event occurs. So, I didn’t have space on the slide for it, but this view would have a showProfileMenu function, and a goHome function. So in our initialize, which, you remeber, was called in the app object’s initialize, we do two things: we bind to our model’s change event, and we render the view. Now the view’s model is the current user object, and we passed that into this intialize.
  42. var myApp = { models: {}, views: {}, initialize: function(currentUserData)

    { myApp.currentUser = new myApp.models.User(currentUserData); myApp.navBar = new myApp.views.NavBar( {model: myApp.currentUser} ); } }); Wednesday, April 25, 2012 ...right here.
  43. myApp.views.NavBar = Backbone.View.extend({ templateName: "navBar", className: "nav-bar", events: { "click

    .profile" : "showProfileMenu", "click .main-logo" : "goHome" }, initialize : function() { this.model.bind('change', this.render); this.render(); }, render : function() { this.template = JST[this.templateName]; this.$el.html(this.template(this.model)); return this; } }); Wednesday, April 25, 2012 Backbone makes this.model, which is the current user, magically available. So what this means is that any time the currentUser object changes, we want to re-render the nav bar. You could imagine that the user updates their profile and changes their first name, so then we need to change it on the nav bar. Now, let’s look at the render function. Render gets the template that has been attached to this view, which you remember was called ‘navBar’, and sets this view’s element’s HTML to the output of rendering that template.
  44. var myApp = { models: {}, views: {}, initialize: function(currentUserData)

    { myApp.currentUser = new myApp.models.User(currentUserData); myApp.navBar = new myApp.views.NavBar( {model: myApp.currentUser} ); } }); Wednesday, April 25, 2012 So coming back to our app object, we’ve set up the current user, and the pieces of DOM that appear on every page. But what about the main content area? On most sites, even those designed as single-page apps, there is a main content area that gets swapped out in response to events, like clicking on the nav bar, or clicking within another view. This is one of those areas where backbone codebases diverge dramatically. There’s no blessed way to do this in backbone, so you’ll see lots of different approaches.
  45. myApp.views.Product = Backbone.View.extend({ el : "#content", events : { "click

    .related": "showProduct", "submit .add": "addToCart" } }); Wednesday, April 25, 2012 Often though it become a ‘graduation’ of the page class pattern - a backbone view for each conceptual page. Does this sound familiar at all? And you don’t see that here, but that view typically has sub-views, and it handles events that occur in its main area, but not in a sub- view. Notice it’s attached to a particular DOM element, and it has a set of events. Like other views it has a render method that’s not show here, that renders its subviews. Now some of these events cause a change of page, like this showProduct event. One way to handle these events is this:
  46. var myApp = { // ... subscribe: function(eventName, handler) {

    myApp.eventNotifiers[eventName] = myApp.eventNotifiers[eventName] || []; myApp.eventNotifiers[eventName] .push(handler); } }; Wednesday, April 25, 2012 ...a global publish and subscribe in your application object. Here’s the subscribe, we’ll look at the publish in a moment. Notice this is inside our myApp object.
  47. var myApp = { // ... publish: function(eventName, data) {

    handlers = myApp.eventNotifiers[eventName]; handlers.each(function(handler){ handler(data); }); } }; Wednesday, April 25, 2012 And here’s the publish function, which just goes through the list of event handlers and calls each one, passing the data that the originating view passes in.
  48. // ... myApp.subscribe(‘changePage’, function(name) { switch(name) { case ‘home’: destination

    = myApp.views.Home; break; }; }; }); Wednesday, April 25, 2012 So then, once you have those, you can attach a general handler to a custom event we can call ‘changePage’. This then has a little mapping, that I don’t show in full here, between changePage params and the views they should change to, and it swaps out the main content view. This requires you to keep the current page as a piece of global state on the app object. And you also need to be careful that you’re removing references to your views when you stop using them, so they can be garbage collected.
  49. myApp.views.Product = Backbone.View.extend({ events: { "click .related": "showProduct" }, showProduct:

    function(e) { myApp.publish(‘changePage’, ‘showProduct’, e.target.data[‘id’]); } }); Wednesday, April 25, 2012 And finally just to close this loop, inside our view that needs to switch the page, we simply publish a changePage event and the app takes care of the rest. There are other ways to do this, some involving backbone’s optional router, which has the advantage of using pushState to change the URL that the user is looking at. Otherwise the URL stays the same until a page refresh.
  50. Photo credit: http://www.flickr.com/photos/isriya/266855445 Wednesday, April 25, 2012 So! .. That

    was a lot of code. That was a whirlwind tour of what I think of as the backbone Full Monty. It’s a very typical way, though, to build a greenfield Rails app these days. But there is a lot of code there. This is the curse part of being lightweight - you need to build a central event handling system yourself, along with a bunch of other code that starts to feel a bit like boilerplate. There are ways to deal with this in larger apps such as creating mixins and base classes, but because backbone has no particular opinion about where code should go, it’s sometimes not obvious where the cleanest place to put stuff is. And particularly if your team is relatively inexperienced with javascript, this can get you into code debt pretty quickly. When I was a newcomer to JavaScript, I found all that code I just showed you pretty opaque and intimidating. But these days, it mostly doesn’t scare me. Mostly. People who are already experienced JavaScript developers usually have no trouble jumping into backbone. And the main point is, if you know your product folks are going for a modern feel, and/or you are building mobile apps at the same time, it’s a huge advantage to have a Rails app as an API that can feed the same data to a native mobile app or to a client-side JavaScript app. If you were in that situation, though, I wouldn’t blame you for wondering if backbone was really the right choice of framework on which to build. I’ll get to that in a bit. But first, I want to talk about what happens if you don’t have this beautiful green field.
  51. Agenda • What is it? • JavaScript & Rails: a

    love story • What backbone gives you • Pattern 1: Greenfield • Pattern 2: Application in transition Wednesday, April 25, 2012
  52. Photo credit: http://www.flickr.com/photos/maunzy/4933328837 Wednesday, April 25, 2012 What if you

    have this instead, and clearcutting isn’t an option? What if you already have an app that’s doing mostly server-side rendering, but you need to move in the modern/API-ish direction? That brings us to the second pattern I want to discuss.
  53. Pattern the Second Backbone as frosting Photo credit: http://www.flickr.com/photos/allieosmar/3516258323 Wednesday,

    April 25, 2012 Backbone as frosting. If you’ve got a Rails app already that’s doing mostly server-side rendering and you need to move towards more client-side rendering, this is where the lightweight-ness of backbone really starts to be an advantage. And actually, the code for this looks pretty similar to the code for the full monty. You still have a myApp object that is instantiated on every page load and sets some global state. The difference is that maybe you put this in your layout, so that on every page refresh you have backbone available. And backbone is very forgiving. If you want some of your backbone views to render client- side templates, and some of them to just overlay server-rendered markup, ok! Backbone’s cool with that. What that means is that rather than clearcutting your app and doing a big rewrite, which is a very very risky thing to do, you can incrementally convert your rendering from server-side to client-side. You can start with a backbone view like this:
  54. myApp.views.NavBar = Backbone.View.extend({ el: "#nav-bar", events: { "click .profile" :

    "showProfileMenu", "click .main-logo" : "goHome" }, initialize : function() { this.model.bind('change', this.fetchNavBar); }, fetchNavBar : function() { $.ajax(...).done { this.$el.html(data); } } }); Wednesday, April 25, 2012 ...which has an ‘el’ attribute rather than a className attribute, and no template. That means it will look for that class in the markup and take it over when the view is instantiated. From then on, it’s as if the markup was rendered by the view itself. You can still use events. Here, we’re fetching the nav bar HTML over again when the current user model changes. This pattern allows you to move your event handlers out of your page objects and in to a backbone view. And if certain events are still handled by other bits of javascript, like by a jquery plugin that is still instantiated page-wide, that’s ok. Backbone’s cool with that. Over time you can replace bits of server side rendering, like this fetchNavBar function, with a client-side template.
  55. Conclusions 1. Backbone works fine on a greenfield application...but consider

    alternatives. 2. Backbone’s sweet spot is when you’re converting an existing rails app to have more client side behavior. Wednesday, April 25, 2012 So we’ve looked in pretty excruciating detail at two different patterns for using backbone within Rails - the first when you have a greenfield project and are dedicated to a fluid app- like feel on the client side, and the second when you have an existing application that you’d just like to make feel more modern. I suppose by now it’s obvious that while I’m completely sold on backbone for case #2, I’m not sold on it for case #1. Backbone is the conservative choice in frameworks, insofar as a JavaScript framework of any kind is a conservative choice. It’s mature, it’s stable, and it has excellent documentation. But there are other, newer, more opinionated frameworks out there, like ember or meteor, that let you write less code. You give up flexibility in exchange for higher velocity - much like you do in Rails. Most of them aren’t quite ready for primetime yet, but if I were starting a greenfield app, I’d take a serious look, because they are all under exceedingly active development.
  56. Thank you! Questions? Sarah Mei @sarahmei sarah@pivotallabs.com Wednesday, April 25,

    2012