Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Using Backbone.js with Rails

sarahmei
April 25, 2012

Using Backbone.js with Rails

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

sarahmei

April 25, 2012
Tweet

More Decks by sarahmei

Other Decks in Technology

Transcript

  1. Backbone.js & Rails:
    Patterns In The Wild
    Sarah Mei
    @sarahmei
    [email protected]
    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.

    View Slide

  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?

    View Slide

  3. What is it?
    MVC
    X
    Wednesday, April 25, 2012

    View Slide

  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.

    View Slide

  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?

    View Slide

  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?

    View Slide

  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

    View Slide

  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...

    View Slide

  9. button_to  "Create",  
       :action  =>  "create",  
       :remote  =>  true
               action="/images/create"
               class="button_to"  
               data-­‐remote="true">
     

    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...

    View Slide

  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?

    View Slide

  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...

    View Slide

  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:

    View Slide

  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.

    View Slide

  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...

    View Slide

  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...

    View Slide

  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.

    View Slide

  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...

    View Slide

  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...

    View Slide

  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 ...

    View Slide

  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.

    View Slide

  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.

    View Slide

  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...

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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. :)

    View Slide

  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.

    View Slide

  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

    View Slide

  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?

    View Slide

  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...

    View Slide

  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.

    View Slide

  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.

    View Slide

  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...

    View Slide

  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?

    View Slide

  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.

    View Slide

  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.

    View Slide

  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?

    View Slide

  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...

    View Slide

  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...

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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:

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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.

    View Slide

  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

    View Slide

  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.

    View Slide

  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:

    View Slide

  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.

    View Slide

  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.

    View Slide

  56. Thank you!
    Questions?
    Sarah Mei
    @sarahmei
    [email protected]
    Wednesday, April 25, 2012

    View Slide