Save 37% off PRO during our Black Friday Sale! »

Backbone.js: Basics & Beyond

Backbone.js: Basics & Beyond

A FluentConf 2012 workshop, presented May 29, 2012 by Sarah Mei of Pivotal Labs.

92497f51734ed56687928d5fd68d870a?s=128

sarahmei

June 16, 2012
Tweet

Transcript

  1. Backbone.js: Basics & Beyond Sarah Mei @sarahmei sarah@pivotallabs.com 1 Saturday,

    June 16, 2012 I’m Sarah Mei, and I’m a Ruby and JavaScript developer at Pivotal Labs, and this is Backbone.js: basics & beyond. Before we get started I want to talk a little about why I’m up here talking to you today. I’ve spent most of the last 6 years doing Ruby on Rails development, and for the past 2 years I’ve been at Pivotal Labs. Pivotal is a consulting company, and my average project lasts about 3 months. I would say, in the last year or so, all of my Rails projects - save one - have used Backbone. That one project that didn’t use backbone.js - it used ember.js. :) More importantly, though, in the last couple of years I’ve seen a dramatic shift in the emphasis of the development of a web application. It has moved away from the server-side code and towards the client-side JavaScript. So I’m up here today because I have used Backbone in a lot of different ways, on a lot of different projects. How many people have been on a project before that used backbone.js? How many folks in here do most of your work in a server-side language, like Java, PHP, .NET, Rails, Python...? Do we have any pure JavaScript folks who stay on the client side and don’t deal much with the server code? Most of my experience is with projects where my team is responsible for both the server code, typically the database as well, all the way down to the client code, and everybody does everything. What the recruiters like to call ‘full-stack developers.’ That used to be weird, but these days it’s getting more common. I went to RailsConf last month and was surprised to find that the talks about javascript were the most popular. That mirrors my experience over the last two years. So let’s get going.
  2. This workshop assumes: 1.You <3 JavaScript 2.You <3 the web

    3.You <3? server-side coding 4.You <3? JavaScript frameworks 2 Saturday, June 16, 2012 Let’s talk explicitly for a moment about what I’m assuming about your existing knowledge. One - I assume you’ve done some JavaScript before. Maybe not Backbone, but you’re familiar with basic JavaScript syntax. Two - I assume you’ve done some JavaScript within a web application, so you’re basically familiar with the interface between JavaScript and the DOM, or document object model, in a browser. My code examples are JavaScript that runs in a browser, provides the user with instant feedback when they take an action, and interacts with a standard REST-ful API (and we will talk a little bit about what that means). I use jQuery in my examples, but they should be comprehensible even if you’re not super familiar with jQuery. Three - I explicitly do not assume that you are doing the server-side coding of the server you are interacting with via JavaScript. That’s where I am personally coming from, but given that this group comes from a lot of different server-side technology backgrounds, I’m going to stick to conceptual stuff in that area, and stay away from the nuts and bolts. And I’ll try not to give too many Rails-specific examples. Four - I explicitly do not assume that you’ve ever used a JavaScript framework on a web application. You may be like me - the first project I went on to that used backbone, I didn’t even notice it was there.
  3. Agenda •Why you need it •Models •Views •Testing •Real-world patterns

    •Templates •Events •Starting the app •Other frameworks •Questions 1:45 - 2:30 (45 min) Basics One 2:30 Short Break 2:35 - 3:15 (40 min) Basics Two 3:15 Coffee Break! 3:45 - 4:25 (40 min) Beyond One 4:25 Short Break 4:30 - 5:15 (45 min) Beyond Two 3 Saturday, June 16, 2012 So here’s the schedule. We have three and a half hours, which is actually... a long time to be in one place at a conference . We have one O’Reilly-mandated half-hour break in the middle, but I’ve added two other short 5-minute breaks to give us some breathing room. Focusing for an hour and a half at a time is pretty hard. Especially after lunch. The first half of the workshop is backbone basics one and two, covering all the basic building blocks of a client-side application in backbone. Then in the second half we’ll get into some advanced stuff. Here’s the extra specific version: We’ll start off with what Backbone is and why you’d want to use it, which to me is one of the least obvious parts of this whole thing. Then we’ll dive into some code to look at models and views. Then after the first break we’ll look at templates, events and bootstrapping the application. Then we have a half-hour break and there will be coffee outside for that one, which I am pretty excited about. At the beginning of the second half, we’ll look at testing and then talk about real-world patterns for organizing backbone code. Then after the last break, we’ll do some comparisons with other related frameworks and put a fine point on what backbone is good at and what it isn’t. I’m planning to have some time at the end for questions, but if we don’t, or if you’d rather talk one on one, my contact information is on the first slide. So let’s get started and set the stage by talking about why you need a JavaScript framework to begin with.
  4. lightweight Photo credit: http://www.flickr.com/photos/mezzoblue/36931701 4 Saturday, June 16, 2012 Backbone.js

    a ‘lightweight’ framework, which distinguishes it from something like 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. I think it’s interesting that the code can end up so different. So during this whole talk, you should keep in mind that any time I show you one way to do something, there are always at least two other ways to do it that are in common usage. Because I’ve seen so many applications in the last few years, I have a pretty good idea of what patterns work under what circumstances. So while this is just my opinion on the right way to do backbone...I, of course, think I’m right. So one of the least obvious parts of something like backbone, for me, as a server-side developer, is .. why do I 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 this part of the talk is the why. And the first thing I want to tell you...
  5. What is it? MVC X 5 Saturday, June 16, 2012

    ...is that Backbone is NOT an “MVC” JavaScript framework. There are a lot of acronyms out there describing patterns for all these different things, but I prefer to think of backbone, and all of these frameworks, really, as...
  6. Photo credit: http://www.flickr.com/photos/knott/21665911 6 Saturday, June 16, 2012 ...cubbies. They

    all have cubbies with different names, and in each cubby, you can put different types of code. And in theory, it helps you keep your code organized. Unfortunately for us, some of the cubbies in backbone have names that are very similar to the names of the cubbies you find a server-side MVC web framework. This is confusing. So let’s break it down.
  7. Available cubbies • Models • Templates • Views Backbone.js: •

    Models • Views • Controllers Server-side framework: 7 Saturday, June 16, 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 a server-side web framework has models, views, and controllers. Now don’t worry, we’re going to draw some lines between the columns so that we can get a good grasp on the differences. But before we do that, let’s talk a little bit about why you’d want to do this. Why marry these two at all? Why do you need a framework to organize your JavaScript when you’re writing a server-side web application?
  8. Photo credit: http://www.flickr.com/photos/gotosira/4699302559 My JavaScript Is Fine Just The Way

    It Is 8 Saturday, June 16, 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?
  9. The Server & JavaScript A Love Story Photo credit: http://www.flickr.com/photos/european_parliament/4973719278

    9 Saturday, June 16, 2012 And to do this, I want to tell you a story about how the relatiopship between server-side code and client side code has evolved. In a lot of ways, this is also the evolution of my relationship as a web application developer with JavaScript. I think a lot of developers coming from the back-end development world go through these same phases, whether they’re doing J2EE or PHP or Rails or whatever.
  10. The Server & JavaScript Phase 1: JS X 10 Saturday,

    June 16, 2012 The first phase is the no-JavaScript phase. This is the phase in which you either don’t do any JavaScript at all, or if you’re in Rails 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...
  11. 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> 11 Saturday, June 16, 2012 So if you do this, at the top of the slide, in Ruby in an HTML view, then it generates for you a piece of rendered HTML that looks like the bottom of the slide. Now this is the nice unobtrusive Rails 3 version, so the key is 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...
  12. link_to_remote  "Add  to  cart",    :url  =>  add_to_cart_url(product.id),    :update

     =>  "cart" 12 Saturday, June 16, 2012 Now this one really is magic. If you put this in ruby in an HTML view, Rails 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. 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. For those of you who don’t know Rails, scaffolding lets you magic up a set of views and a REST-ful controller for a resource. It’s what David Heinemeier Hansson used in the famous ‘build a blog in 5 seconds’ (or whatever it was ;) screencast he did. Anyway, that sort of thing is great for demos, and it’s useful for whipping something up fast at a hack night, but like these JavaScript helpers, it’s not something you use in real applications. As an industry, we are at the point now where JavaScript needs to be a first- class citizen in our web applications. If you’re at Fluent, I probably don’t need to convince you at that. But one interesting question is why are we at this point?
  13. Image credit: http://i.chzbgr.com/completestore/12/4/22/A3M8QMvalEiXdwuJP5U4xA2.jpg 13 Saturday, June 16, 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 that kind of simple JavaScript helper 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...
  14. JQuery explosion Phase 2: The Server & JavaScript 14 Saturday,

    June 16, 2012 ...the jQuery explosion. This is what happens when you convert your Rails helper calls to actual JavaScript. If you’re using a server-side framework that doesn’t ‘help’ you write JavaScript, then this is usually where you start. It’s called the jQuery explosion because you start getting stuff like this at the top of each of your HTML pages:
  15. $(function()  {    $(‘.username’).autocomplete();    $(‘.create-­‐form’).submit(function(){        

     $.ajax(...).done(function(data){              $(‘.yay’).html(‘good  jorb!’);              $(‘.count’).html(data[count]);              /*  other  stuffs  */        });    }); }); 15 Saturday, June 16, 2012 As you can see we’re calling a jQuery plugin right at the top for autocompleting the username field; your JavaScript quickly gets littered with those as you find a plugin that does this and a plugin that does that... And then underneath that, we’re doing some pretty standard ajax stuff. We have an HTML form with class ‘create-form,’ and we’re attaching a callback to its submit function, which ajax-ily submits the form, and when that form submit call returns, it updates some stuff in the DOM based on the data that comes back. This is the behavior of just one form - but there’s never just one form on the page. You can imagine that with several forms, maybe some inputs that do validation against the server, etc., this gets unwieldy pretty quickly. Under “standard” server-side MVC, your views are supposed to be dumb, and have no logic. But as your product folks ask for more and more client-side interactions, you get a bunch of JavaScript at the top of every view, and the views start getting...
  16. Photo credit: http://www.flickr.com/photos/puyo/253932597 16 Saturday, June 16, 2012 ... messy.

    Now, with this picture, I am NOT making any kind of statement about whether our physical work environment imprints itself upon our code. Just saying. A lot of web applications basically stop at this phase. They may pull the js out into a file and include it in their HTML page, but it’s still messy. The main drawback, of course, is not exactly aesthetics - it’s that it’s hard to test. Integration tests - selenium, or one of the headless solutions - are the only way to exercise it, and those are sloooow. Even the headless ones are slooow, compared to being able to unit-test the JavaScript. So as the interaction quotient of our application goes up, the tests get slooower. The other option, of course, is to not test the JavaScript, 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 had at this problem, and a lot of them came up with the same solution...
  17. Page Objects Phase 3: The Server & JavaScript 17 Saturday,

    June 16, 2012 ...Page objects. Phase 3 of our love story! 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...
  18. MyApp.FriendRequestNew  =  {  initialize:  function()  {      $(‘.username’).autocomplete();  

               $(‘.create-­‐form’).submit(doCreate);  },  doCreate:  function()  {    $.ajax(...).done(function(data){      $(‘.yay’).html(‘good  jorb!’);      $(‘.count’).html(data[count]);      /*  other  stuffs  */    });  } }; 18 Saturday, June 16, 2012 Here’s the page object that re-implements the JavaScript that we previously had thrown into the view. It’s just a plain old JavaScript object - notice we’ve created a namespace called MyApp on which we collect a series of page objects. It has an initialize function, and we’ve pulled out the anonymous function that we had been passing to submit, and given it a name. When you call initialize in your page, you’re basically doing the same thing as that page full of code before. So - it does the same stuff - and you’ll also notice that is not a whole lot less code than what we had before. But now we can unit-test this JavaScript object in qunit or jasmine or whatever, rather than having to rely on integration tests. So we write tests for the doCreate function to make sure it submits the right stuff to the server, and to make sure it does the right stuff to the DOM when the request returns. And we write a separate unit test for the initialize function that makes sure doCreate is called when the submit event is triggered in the DOM. The ability to unit test your JavaScript is huge. Unit testing is really fast. At Pivotal we have a project that has 3,000 individual Jasmine tests - it’s a rich client-side app - and all of those tests together take about a minute and a half to run. With integration tests on an app like that, to get equivalent coverage, the suite would take about 2 hours. Now you might ask, who cares if your tests are fast or slow? The point there is that if your JavaScript tests are fast, all the developers - whether they’re working on server-side code or on the JavaScript - will run the tests before checking code in. The end state: fewer annoyed “you broke my stuff” emails. Everyone wants that. So once you convert your application’s JavaScript to page objects, you usually start packaging behavior that is common so that it’s easy to apply to a given page, something like this:
  19. MyApp.FriendRequestNew  =  {  initialize:  function()  {    MyApp.NewFriendForm.initialize();    

         MyApp.MyFriendsSummary.initialize();    MyApp.SearchBox.initialize();  }     }; 19 Saturday, June 16, 2012 You start breaking your pages down into components, or widgets, and the page class becomes the thing that calls initialize on each of them in document.ready. So that JavaScript that you had just thrown into the view actually does get nicer looking. It goes...
  20. $(function()  {    $(‘.username’).autocomplete();    $(‘.create-­‐form’).submit(function(){        

     $.ajax(...).done(function(data){              $(‘.yay’).html(‘good  jorb!’);              $(‘.count’).html(data[count]);              /*  other  stuffs  */        });    }); }); 20 Saturday, June 16, 2012 ...from this...
  21. $(function()  {  MyApp.FriendRequestNew.initialize(); }); 21 Saturday, June 16, 2012 ....to

    this. And this is much, much nicer. Now this pattern can take you pretty far. In Rails- land there are even gems 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. There are a lot of web applications out there for which the page object pattern is sufficient. At this point you can unit test your JavaScript, you can reuse common interactions, and the code is pretty well organized. The page class pattern starts to break down when your product people decide they want to go for a single-page-app “feeling” in your web application. In development terms, the pattern breaks down when you start doing client-side view rendering. We’ll talk a little more in a moment about what that means. So: page classes are awesome, and probably sufficient to organize and test your JavaScript, if you’re still doing server-side view rendering.
  22. 22 Saturday, June 16, 2012 So let’s break that down

    a little bit. What does single-page-app actually mean? People usually think of Gmail or Twitter as canonical single-page apps. Here’s Twitter, which I’m sure none of you use, but if you click on one of these links...
  23. 23 Saturday, June 16, 2012 ...it opens in place. And

    if you want to look at someone’s profile, which in a traditional web application is a page refresh, it instead...
  24. 24 Saturday, June 16, 2012 ...opens on the same ‘page‘

    in a modal dialog. So from a user point of view, they never see a page refresh. Apparently this is what kids today like. In practical terms, when a product person requests a “single-page-app feeling”, they’re asking for more page interactivity than you can reasonably do with just ajax and redirects. When you click a link, like a profile link on twitter, here, it renders a decent chunk of markup - the stuff in the modal, because you know this wasn’t all rendered ahead of time and then just shown when you clicked the link - without refreshing the page. So you start considering ...
  25. Image credit: http://www.flickr.com/photos/whatsthatpicture/6368412285 25 Saturday, June 16, 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 once you’re doing client-side rendering, page classes break down pretty quickly, because you start trying to attach templates to page classes, or to their subcomponents, and then you start needing to know 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 maintain some state between actions. You start wishing for some organized way to associate a template with a component and re- render it in response to events.
  26. Image credit: http://www.flickr.com/photos/quasimondo/3892920130 26 Saturday, June 16, 2012 And speaking

    of events, you’re generating a lot of them. You’re generating events inside the sub-components of your page class. Inevitably you have one component on the page with DOM events that affect other components on the page. For example, on twitter, when you click the follow button next to someone’s name, it needs to show you a success message, update the number of people you’re following, and add that person’s messages to your existing timeline. How does the little component that controls the follow button let all these other components know what happened? This is particularly messy when you’re using jQuery and other cross-browser libraries, because DOM events only bubble up. So if you have a hierarchy in which you have a page class with a number of sub-components, and sub-component A generates an event that sub- component B, its sibling, needs to consume, it’s up their common parent object - 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.
  27. Photo credit: http://www.flickr.com/photos/kirkt/127384374 27 Saturday, June 16, 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 web application developers into...
  28. Frame works Phase 4: The Server & JavaScript 28 Saturday,

    June 16, 2012 ...the arms of JavaScript frameworks. Now, there are a lot of frameworks out there, which all solve these problems in different ways.
  29. Backbone.js Cappucino Ext.js AngularJS Knockback.js Agility.js Sammy.js Knockout Spine.js JavaScriptMVC

    Ember.js Sproutcore Google Web Toolkit Batman.js Google Closure Meteor YUI troopJS olives dijon Fidel 29 Saturday, June 16, 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. We’ll talk a little bit at the end of this workshop about how backbone compares to some of these other frameworks, but for now you should know that these frameworks all address different subsets of the problem of building a large JavaScript application. Not all of them are designed to help you build the interactive client-side of a web application - you can just look down the schedule here at Fluent to see that there are LOTS of other things you can write in JavaScript these days. And, even among the set of frameworks oriented towards client-side web applications, different frameworks solve the common problems differently. And we will talk a little more about that towards the end of the workshop, once you’ve seen more of what backbone actually does.
  30. What Backbone gives you • Model mirroring (Models & Collections)

    • Views that handle events (Views) • & maybe render DOM (Templates) 30 Saturday, June 16, 2012 So we’ve gone through the evolution of JavaScript in web applications. So now you have an idea of problems frameworks are trying to solve. Let’s get back to how backbone addresses those problems. First 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. Second, it gives you views that handle DOM events, and they can also, optionally, be attached to a template that renders markup.
  31. Available cubbies • Models/Collections • Templates • Views Backbone.js: •

    Models • Views • Controllers Server-side framework: 31 Saturday, June 16, 2012 So let’s come back to this model-template-view-controller comparison chart. As I mentioned just now backbone.js gives you model mirroring, or more specifically resource mirroring, using objects it calls models and collections. Conceptually, the models are actually about the same on both sides. They’re meant to encapsulate a business object of some kind. A Tweet. A User. A Comment. Now, to be really technical about it, a backbone model usually mirrors a resource. The set of backbone models thus ends up mirroring your set of RESTful API endpoints.
  32. Server Server models: Browser Backbone models: internet cloud thingy Message

    User Friendship Message Friend User CurrentUser /current_user /users /friends /messages 32 Saturday, June 16, 2012 Your RESTful API endpoints may not correspond 1-to-1 with your server-side models. Let’s take the example of a social network in which there are messages, users, and friendships on the server side. Friendships represent the relationship between two users - it’s a join table. This is actually the app we’re going to build a tiny part of today. Messages are pretty straightforward - you can expose a message resource that corresponds more or less directly to your server-side model. Then it would make sense to have a backbone model called Message that mirrored that resource endpoint. It’s not so straightforward when you come to users and friendships. You might not expose friendships as a resource. Instead you might expose a resource called friends, which is not its own model on the server side, but is user data with extra info about the relationship added on. Likewise User is exposed directly as /users, perhaps for autocomplete, but it also exposes a singleton resource for the current user. So it would probably make sense, on the backbone side, to mirror the endpoints rather than the models, and have user, current user, and friend models.
  33. Available cubbies • Models/Collections • Templates • Views Backbone.js: •

    Models • Views • Controllers Server-side framework: 33 Saturday, June 16, 2012 So that’s models. A collection, in backbone land, corresponds to an array or more broadly a set of backbone models of the same type. Now here’s where the terminology starts to get confusing. “Views” in server-side MVC framework correspond to “templates” in backbone. In backbone, the template is the logic- less interpolated HTML. There is no arrow between “views” on the server 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 I was still thinking of a view from the server-side perspective. So I’m trying to spare you that learning curve. :)
  34. 34 Saturday, June 16, 2012 So that’s the end of

    our intro, so I thought it might be nice to look at some scenery. Take a deeeeeep breath. \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 reminder to breathe? Mostly for me. You’re welcome to take a deep breath too, though. Ok! Let’s launch into the basics of actually building a backbone application.
  35. Backbone Basics 1. Get data from the server 2. Render

    HTML elements 3. Handle events 4. Set up the app AKA: models & collections views & templates events what do I put in document.ready? Task: 35 Saturday, June 16, 2012 There are three basic parts to building a backbone app: 1. getting data from the server (models & collections), 2. rendering HTML elements based on that data (views & templates), and 3. events. And lastly there is actually getting it started. :) So let’s dive right in and start looking at code!
  36. https://github.com/sarahmei/tiny-api-app 36 Saturday, June 16, 2012 Here is a github

    repo that may be useful - this is a tiny rails app that emulates the messages endpoint of that diagram we looked at earlier.
  37. 37 Saturday, June 16, 2012 So let’s look at my

    bad balsamiq mockup for the application we’re going to build. There’s a lot here on this mockup - a lot of features - so we’re going to focus on just getting everybody’s status updates into the main area of the page. That’s this part in red. We’re not going to worry about following, friending, logging in, or the sidebar. Let’s just focus on getting it to the point where it displays everyone’s messages. MVP baby!
  38. Magic API Server Server models: Message User Friendship /current_user.json /users.json

    /friends.json /messages.json 38 Saturday, June 16, 2012 Here’s the basic structure of our magic API. I call it magic, because we’re going to just assume it exists and responds to our requests. I did write a little rails app that serves / messages.json, and that’s on my github, but you should only try to get it running if you know you’re already set up for rails apps. So. We’re building a client-side application for creating, viewing, and responding to status messages. We’re going to assume that this RESTful API exists and responds to our requests. Now as I said earlier, we’re not going to think about at the server code that generates our API. We’re just going to assume it works.
  39. 39 Saturday, June 16, 2012 So our magic API application

    responds to two formats: HTML and JSON. If you don’t specify, it gives you HTML. Here’s what it looks like if you just hit /messages - you get a normal server- side rendered page with a list of all status messages in the system.
  40. 40 Saturday, June 16, 2012 But if you hit it

    with .json on the end of /messages, you get the JSON representation of that same data. Notice it’s got the same two messages, with all their metadata. This is what our client-side app will consume. The HTML representation is mostly a convenience so we can easily enter data before the client-side is built. With some APIs, though, the only option is JSON.
  41. Real quick REST GET POST GET GET GET PUT DELETE

    /messages.json /messages.json /messages/new.json /messages/:id/edit.json /messages/:id.json /messages/:id.json /messages/:id.json show all (index) create new edit one show one update one delete one Verb URL Resulting action 41 Saturday, June 16, 2012 I want to pause for a moment and talk briefly about REST. I’ve been throwing that term around a lot so I think a quick refresher is in order. Now, this is very, very simplified - I originally titled this slide “REST 101” but I realized it’s more basic than that. This is the super quick & dirty version. REST is a way to organize your API endpoints around the idea of “resources.” A resource is something like a message, or a user. When you implement a RESTful API for a message, it looks something like this. Now, the way to read this, is that there are seven actions you can take on messages - that’s the last column. These actions are divided into actions you take on a single message - edit, show, update, and delete all operate on a single message, and those are in green - and actions you take that aren’t associated with an individual message. Those are in purple. For example, seeing a list of messages, or creating a new message, is not associated with any particular existing message. In REST, each one of these seven actions is associated with a different location on the web. Now given the same seven operations, you could organize this differently - for instance, you could have one endpoint, /messages, and make your users pass in what action they wanted to do as a parameter. But REST specifically means that the way you organize it is to have a different location for each different action. Now those of you with good eyes have probably noticed that the URL in the middle column is not always unique. For example, “show all” and “create” share the same URL, /messages.json. In that case we distinguish them via the HTTP verb, which is the first column. In web standards land, the same URL with a different verb is a different “location.” So each endpoint in this list is actually at a different location, because the URL is the same in some cases, but the combination of URL and verb is unique.
  42. 42 Saturday, June 16, 2012 Ok, so, we have this

    JSON coming back from the server. We want to use that data to build...
  43. 43 Saturday, June 16, 2012 ...this. Sure no problem! Let’s

    get going.
  44. Models Image credit: http://www.flickr.com/photos/mmorgan8186/6102125648 44 Saturday, June 16, 2012 Now

    that we have a RESTful JSON API we can hit, let’s look at some JavaScript! (Finally!) First up: models.
  45. Models var Message = Backbone.Model.extend({}); 45 Saturday, June 16, 2012

    Here’s our relatively straightforward Message model. Nice and simple to start with. It’s a JavaScript object that ‘extends’ Backbone.Model - that is how backbone does inheritance. Anything that you pass to extend will override Backbone’s defaults. In this case we are happy with the defaults, so we pass in an empty object, {}. Now, for what we’re trying to do, creating an individual message model object makes less sense than creating a collection of message model objects. What we’re trying to do, after all, is see them all on one page. But now that we have a message model, we can create a message collection, which is a set of message models. A collection in Backbone is a separate thing.
  46. Collections var Messages = Backbone.Collection.extend({ model: Message, url: "/messages" });

    46 Saturday, June 16, 2012 So here’s a backbone collection that we’re calling Messages, plural - remember our model was called Message, singular. And here, we are ‘extending’, or ‘inheriting from’ Backbone.Collection instead of Backbone.Model. We are also passing some stuff into that call to extend, and these things will override backbone’s defaults. We’re overriding two things: first, we’re telling it what model its items are, and then we’re giving it a URL. Now this is where that REST slide we were looking at comes into play, because you’ll notice we aren’t giving it the full set of seven actions - we’re only telling it about / messages. But from that, it will figure out how to access the other actions, and it will tell its individual message model objects how to show and update themselves. So. How do we get the set of messages into the collection in the first place?
  47. var mainMessages = new Messages; mainMessages.fetch(); Collections 47 Saturday, June

    16, 2012 Here’s how you create a collection object, and fetch its messages from the server. Fetch knows to go to its URL, “/messages”, which you remember we assigned in the Messages Collection.
  48. Collections mainMessages.fetch() Server Server models: Message User Friendship /messages.json GET

    JSON 48 Saturday, June 16, 2012 So to diagram it out explicitly, when you call fetch, it goes to our server, gets JSON back representing an array of messages, and then it creates a Message model object for each object in that array. So that lets us avoid writing all those direct calls to ajax in our JavaScript - one for each of the seven operations, which would all need to be written and tested individually. So, that’s awesome! We have a collection of messages, representing all the messages on the server. It turns out, though, that this doesn’t do us much good without a view. Views are what generate the HTML and put it in the DOM, so right now we have no way on the client side to see our collection of messages, except for looking at it in the JavaScript console. Still - progress!
  49. Backbone Basics 1. Get data from the server 2. Render

    HTML elements 3. Handle events 4. Set up the app AKA: models & collections views & templates events what do I put in document.ready? Task: ✓ 49 Saturday, June 16, 2012 We have now checked off the first part of backbone basics, and which also brings us...
  50. Image credit: http://www.flickr.com/photos/adhe55/3762266355 Views 50 Saturday, June 16, 2012 We

    wrote a message model and a message collection. We’re going to leave them aside for a moment and start looking at backbone views. Now if you remember from the introduction, in backbone, a view is an object that conceptually controls a certain area in the DOM. In concept, it’s less like how server-side developers think about a view, and more like how iOS or cocoa developers think about a “view controller.”
  51. 51 Saturday, June 16, 2012 So, looking at what we’re

    trying to build, we can sorta see how this part in red could be one view. Maybe if we wanted to get really fancy, which we won’t in this workshop, we could break each repeating area within it into a subview. We’re just going to completely ignore the sidebar. I had fun making the mockup, though. Let’s start with a top-level backbone view for that whole area in red.
  52. var MessagesView = Backbone.View.extend({ className: "messages-container" }); Views 52 Saturday,

    June 16, 2012 Here’s our view prototype, which extends from Backbone.View. We pass in (for now) only one override of its defaults. This is the class name of the DOM element that this view is in charge of. Now backbone is very flexible, so when you’re constructing a view, you can either pass in an element that already exists on your page, which it then takes charge of, or you can tell it to create a new element. In this case we’re telling it to create a new div (a div is the default type of DOM element it creates, but you can make it create something else) and it will put the class “messages-container” on the div it creates.
  53. var messagesView = new MessagesView; Views 53 Saturday, June 16,

    2012 This is how we create an instance of that view. When we do this, it creates a div element with class “messages-container,” and holds on to that element, but it does not insert it into the DOM. That’s on purpose; backbone wants you to have control over when repaints happen in the browser. JavaScript is a very fast language, as long as you don’t touch the DOM. So on big applications, people tend to take pains not to insert or remove or change anything in the DOM unless they have to. We’ll come back to that when we get into subviews. So now, our messages container element exists, but it’s very boring. It’s empty, in fact. That’s because we haven’t give this view object any data to work with. All we did was tell it about what DOM element we want it to control. So let’s combine this code with the Messages collection that we were working on earlier.
  54. Views var mainMessages = new Messages; mainMessages.fetch(); var messagesView =

    new MessagesView({ collection: mainMessages }); 54 Saturday, June 16, 2012 So. This top part should look familiar - it was only a few slides ago, come on people, stay with me! This is where we create a collection of message models, and fetch data from the server to populate it. The fetch call on the second line makes an ajax request to our server, gets JSON back representing the list of messages, and creates individual Message model objects for each one of them. Ok! This second part is where we create the view that displays those messages. Now we are passing in some data: we are telling it that its collection is the one called mainMessages that we just created. “collection” is a magic parameter for a Backbone view - a view can either take a collection, like we are doing here, or it can take an individual model object. For example, you could imagine a profile page view object that takes, as its model, the individual user whose profile you’re looking at. But in this case we’re giving it our messages collection. Now our view has the data it needs, but we still have some work to do. Backbone does not automatically render the data you give it. You need to have a render method that does that. And that will bring us to templates. And that also brings us...
  55. Agenda •Why you need it •Models •Views •Testing •Real-world patterns

    •Templates •Events •Starting the app •Other frameworks •Questions 1:45 - 2:30 (45 min) Basics One 2:30 Short Break 2:35 - 3:15 (40 min) Basics Two 3:15 Coffee Break! 3:45 - 4:25 (40 min) Beyond One 4:25 Short Break 4:30 - 5:15 (45 min) Beyond Two 55 Saturday, June 16, 2012 ...to our first break.
  56. Image credit: http://www.flickr.com/photos/polegario/238020211 56 Saturday, June 16, 2012 Break 1:

    cats.
  57. Image credit: http://www.flickr.com/photos/nagillum/62550677 57 Saturday, June 16, 2012 Break 1:

    cats.
  58. Agenda •Why you need it •Models •Views •Testing •Real-world patterns

    •Templates •Events •Starting the app •Other frameworks •Questions 1:45 - 2:30 (45 min) Basics One 2:30 Short Break 2:35 - 3:15 (40 min) Basics Two 3:15 Coffee Break! 3:45 - 4:25 (40 min) Beyond One 4:25 Short Break 4:30 - 5:15 (45 min) Beyond Two 58 Saturday, June 16, 2012 Let’s get back into it.
  59. 59 Saturday, June 16, 2012 Before we get into rendering

    and templates, I want to surface for a moment and talk about some JavaScript language stuff that we’re doing, that confused me for a long time. It’s a little abstract, but I promise, this will be useful later.
  60. Prototypes != Classes var  MessagesView  =      Backbone.View.extend({  

         className:  "messages-­‐container" var  messagesView  =  new  MessagesView({    collection:  mainMessages }); /*  ...  */    }); initialize:  function()  {/*...*/} 60 Saturday, June 16, 2012 So! I want to talk about constructors, prototypes, and instances. The headline is the short version and those of you who have done JavaScript for a long time are probably thinking, .. “thank you, captain obvious.” But I come from an object-oriented class-based language background - before I did Ruby, I did Java. In an object-oriented language, we create a ‘class definition,’ and then create instances of that class using that definition. JavaScript has prototypes instead, which feel a lot like classes but have some important differences. So here is the ‘class definition’ - that’s how I thought of it for a long time - that we wrote for our backbone MessagesView. After this statement runs, the var capital-M MessagesView is actually just an object, like any other JavaScript object, with attributes and values. It’s not a special class object, or anything wacky like that. It’s just an object. Prototyping means that to create “instances” of this “class,” JavaScript essentially just clones this object. This prototype is extending Backbone.View, which gives it a bunch of properties that we don’t explicitly specify, including one called initialize, which is a function. When we create a new instance of the view from this prototype object, that function is called. So here’s how we created a new instance of the MessagesView collection - using the new operator. When we did that, Backbone looked into the MessagesView prototype object, got its initialize function, and called that function passing in any arguments that we give to new. And what that did is basically clone the prototype and then apply any overrides that we pass in. Now the key is that we can use the arguments we pass in here, at the bottom, when we’re creating an instance, to override anything that is up here at the top, in the prototype. So up here, where we are creating the prototype, we set the stuff that is going to be true for pretty
  61. Image credit: http://www.flickr.com/photos/nicecupoftea/83838625 61 Saturday, June 16, 2012 why should

    you care?
  62. var MessagesView = Backbone.View.extend({ className: "messages-container" }); var messagesView =

    new MessagesView({ className: "something-else" }); Prototype Instance 62 Saturday, June 16, 2012 Well, in JavaScript, defining a prototype and creating an instance of that prototype look very similar, and it’s easy to get them confused when you’re not a JavaScript wizard. Or, when it’s the middle of the afternoon and you’re looking at code from 50 feet away. So by convention: if it starts with a capital letter, it’s a prototype. If it starts with a lower-case letter, it’s an instance.
  63. Templates Image credit: http://www.flickr.com/photos/robglinka/6041889401 63 Saturday, June 16, 2012 We’re

    now approaching the exciting world of rendering & templates. This can be one of the most dense/obscure parts of backbone. We may need to take cat picture breaks as we’re going through it, we’ll see. Let’s first talk about rendering.
  64. var  messagesView  =  new  MessagesView({    collection:  mainMessages }); Rendering

    64 Saturday, June 16, 2012 Right before the break, we had done this. We had created an instance of the view class and given it the data to work with - in this case the collection of messages that we had already fetched from the server. Now we need to tell it how we want that data displayed. Now backbone views have a render function built in, but the default render function does nothing. So that’s where we’re going to put the code that actually creates DOM elements from the data. Now the render code is probably going to be more or less the same for every instance of this messages view. So we’ll add it to the prototype.
  65. Rendering var  MessagesView  =      Backbone.View.extend({      

     className:  "messages-­‐container" });                                                              , render:  function()  {  this.collection.each(function(m){    this.$el.append("<div>"  +        m.get("text")  +  "</div>"); }} 65 Saturday, June 16, 2012 So first I’m going to show you the simple way to do render. Here’s our prototype for the MessagesView - let’s add a render function. Backbone’s templates are optional. So here, we are just iterating through our collection, and for each one, appending a div with the message’s text in it to element. A few things to point out: “this.$el” is a reference to the jQuery-ified element that the view has already created. And “m.get(“text”)” is how you get and set properties on a model object. In this each loop, m is a Message model object that has a property called text which came from the json from the server. There are a lot of problems here. Chief among them is that we want actually a pretty complicated markup structure, not just a div with the text thrown in. Going back to our wireframe...
  66. 66 Saturday, June 16, 2012 Woah, the product people have

    updated it, that timestamp didn’t used to be there. :) So each one of these individual messages is a decently complex piece of markup. It’s showing the text of the message, and it’s also showing the picture of the user who posted the message, and a timestamp that links to something. I don’t think we’re going to get to all that today, but I think you can see why you start wanting a template to use, instead of manually building up elements in the render function. Backbone is agnostic about the type of templates you use; my projects tend to use mustache, but there are lots of others available. There is one built in to underscore.js, a utility library that backbone depends on. It uses erb-like syntax; for simplicity, that’s what I’ll be showing here.
  67. Rendering with Templates var  MessagesView  =      Backbone.View.extend({  

       template:  _.template(T["message"]),      className:  "messages-­‐container" });                                                              , render:  function()  {  this.collection.each(function(m){    this.$el.append(      this.template({msg:  m.toJSON()})) }} 67 Saturday, June 16, 2012 So let’s add another thing to our view prototype: a template. So there are a couple of new things here. First there’s this third line: this is how we specify the template, and we’ll get into what all that means in a minute. But also, our render has changed. Notice that instead of building up a string of HTML, we are calling the template like it’s a function and passing it the message model object. And that is because a template in backbone is a function that returns a string. Now let’s unpack that top line a little. _.template, yes, that’s an underscore as a variable name. We’re calling its template function and passing it a string, and it compiles that string into a function we can call as many times as we want inside of render. The mechanics of the compilation are a bit complex, but you can think of capital-T as an object that holds all the template strings. It might look something like this:
  68. Rendering with Templates var  MessagesView  =      Backbone.View.extend({  

       template:  _.template(T["message"]),      className:  "messages-­‐container" });                                                              , render:  function()  {  this.collection.each(function(m){    this.$el.append(      this.template({msg:  m.toJSON()})) }} var  T  =  {  message:      ‘<div><%=  message.text  %></div>’ }; 68 Saturday, June 16, 2012 Now how you get your templates the T object, since of course you’d like to work on them as separate files and not have to do the ugly string concatenation thing just in a different place, varies by technology. We’ll talk a little bit about that in the starting-your-app section.
  69. <div  class=‘photo’>  <img  src=‘<%=  message.photo_url  %>’/>   </div> <div  class=‘text’>

     <%=  message.text  %> </div> <div  class=‘time’>  <%=  message.created_at  %> </div> Rendering with Templates 69 Saturday, June 16, 2012 Ok! So now, if we modify our template to look something more like this, then...the pieces are together. We’ve successfully fetched a set of messages from the server and rendered them into DOM elements on the client side.
  70. var  MessagesView  =    Backbone.View.extend({    /*  other  stuff  omitted

     */    initialize:  function()  {      this.render();    }  }); Rendering with Templates 70 Saturday, June 16, 2012 Now one last thing - we do need to call render somewhere. We’ve got a render function that creates DOM elements from templates and inserts them into the DOM, but backbone doesn’t call render on its own. You’ll see many different patterns for this, but one common one is just to render as part of creating a new view object. To do that, we come back to our MessagesView prototype, and add an initialize method, to override the one that Backone.View gives us. And it’s pretty simple - all it does is render itself.
  71. var mainMessages = new Messages; mainMessages.fetch(); var messagesView = new

    MessagesView({ collection: mainMessages }); Coming back... 71 Saturday, June 16, 2012 So! That covers rendering and templates. Let’s go waaaay back into history...remember this code? This is how we get started. We create a Messages collection called mainMessages, call fetch on it to get messages from the server, and then make a new view and pass in the collection. Now, thanks to our undercover work, when we call: new MessagesView and pass it a collection, it will actually render that collection into the DOM. That’s pretty cool. So now we’ve dealt with:
  72. Our Progress So Far 1. Get data from the server

    2. Render HTML elements 3. Handle events 4. Set up the app ✓ ✓ AKA: models & collections views & templates events what do I put in document.ready? Task: 72 Saturday, June 16, 2012 1 and 2...check. We got through that even without having to resort to cat pictures! Awesome! Let’s talk about...
  73. Events Image credit: http://www.flickr.com/photos/whalt/350962472 73 Saturday, June 16, 2012 Events!

    So, if you remember from the introduction, besides all the ajax calls back to the server that you had to write every time, the major pain of page classes was dealing with events. There are three levels of event handling that we’re usually interested in:
  74. Events A. Input events B. Data-change events C. Application events

    low level high level 74 Saturday, June 16, 2012 First are input events. How do we attach behavior to input events like clicks, focus changes, form submits, and so on? These are DOM-level events generated directly by user input. Second are data change events. So, for example, if we have a collection of messages and it’s rendered into a list in the page, when more elements are added to that collection, something should probably happen. This level deals with that type of event. And third are application-level events that frequently need to be communicated between components. These events are things like “user followed someone new” or “user logged in” or “user posted a message.” I think of all three types as being on spectrum from low level events coming directly from the DOM, up to the high-level events, where even the set of events you’re interested in changes with the application. Backbone does a decent job with set A and set B - helping you easily attach behavior to events within a view, and to data change events - but it doesn’t address set C at all. Let’s talk about how you attach behavior to input and data-change events first, and then we’ll look at how large apps tend to address higher-level events. Starting with input events! Let’s look at our wireframe again and see if we have any.
  75. 75 Saturday, June 16, 2012 The product folks have decided

    that when you hover over that timestamp link, you should see, in a tooltip, the absolute time instead of the relative time. So if the timestamp says “about an hour ago” and it’s 7:15pm, when you hover over it, you see a tooltip that says 6:10pm. Let’s assume we get that data from our API, as part of the message model fetching. There are a couple of ways we could approach this. The simplest is probably just to not use Backbone’s event system at all, and instead use use a jQuery plugin to take care of it - there are a few out there that deal specifically with this problem. Here’s where Backbone’s flexibility and minimalism come into play, because it will play nice, no problem, with jQuery plugins intercepting events in its DOM elements. So when I say that a backbone view “controls” a particular area in the DOM, ah, perhaps “lightly supervises” would be more accurate. For instance we could totally do this:
  76. var  MessagesView  =    Backbone.View.extend({    /*  other  stuff  omitted

     */    initialize:  function()  {      this.render();      this.$(".time").timeAgo();    }  }); Input Events 76 Saturday, June 16, 2012 The new code here is circled - in initialize, we are calling a jQuery plugin called “timeAgo” on the set of elements with class “time.” Now this doesn’t look like much, but it’s quite powerful. Since you can mix jQuery plugins with backbone events, you can move existing JavaScript on an application over to Backbone gradually. I’ve actually come to think that that is backbone’s sweet spot - moving an existing web application more towards a single-page-app feel. We’ll get back to that in the last section when we talk about other frameworks. So let’s say we wanted to do this the Backbone way, without the plugin - maybe we couldn’t find a plugin with an appropriate license, which happens to me a lot actually. Let’s see what that would look like.
  77. var  MessagesView  =    Backbone.View.extend({    /*  other  stuff  omitted

     */    events:  {        "hover  .time":  "showTooltip"      },    showTooltip:  function(){  /*  ...  */  }    }); Input Events 77 Saturday, June 16, 2012 So - we took out the jQuery plugin stuff that we added on the last slide, and here’s what we replaced it with: an object called events where: - The keys are strings consisting of an event name, in this case “hover”, and a selector, in this case “.time”. - The values - in this case “showTooltip” - are strings containing names of functions to call whenever those events occur. Declaring this event means that whenever Backbone sees a hover event within this view’s DOM area where the event target is something with class .time, it will call showTooltip. I’ve left out the implementation, but you can see that the prototype now has a showTooltip function. You can, of course, have multiple events declared like this in your events object. It’s really handy for every backbone view prototype to have a declarative list of events it cares about at the top of its implementation. I really like how backbone deals with input events. So let’s talk about data change events!
  78. Data Events var  MessagesView  =    Backbone.View.extend({    /*  other

     stuff  omitted  */      initialize:  function(){      this.render();      this.collection.on("change",            function(){  this.render();  }      );        }    }); 78 Saturday, June 16, 2012 Backbone lets you, within views, bind to change events on data objects, so that you can take action when the data changes. The most common action is to re-render the view. In our MessagesView, we probably want to re-render the view whenever the collection changes, so we add this to our initialize. As you can see, we’re binding some behavior to a Backbone event called “change” that is fired whenever the collection...changes. On collections, that “change” event is fired if an item is added to or removed from the collection, and it is also fired when attributes change on an individual model within the collection. Backbone has other, more specific data-change events you can bind to. Finally, let’s talk about application events.
  79. Application Events this component gets success from its ajax call

    but this one needs to know about it too 79 Saturday, June 16, 2012 Our last type of event is application events. These arise when components within a page need to communicate with each other. Let’s say, for example, that a user successfully logs in. The component that receives the success message from the server - probably down here in the body of the page - needs to notify the nav bar, so the nav bar can display the user’s name. These are the events that backbone has no structure to deal with. This is intentional - remember that backbone is ‘lightweight.’ It deliberately leaves out infrastructure that you will need if your app reaches any appreciable size. The consequence, though, is that different applications built their own infrastructure in vastly different ways. Let me enumerate some of the approaches I’ve seen for application-level events.
  80. Application Events Application Events • Making everything a data event

    • Giving each component handles to other components it needs to notify • Having the top-level view catch events and propagate them down as needed • Implementing a top-level publish/subscribe X X X ✓ 80 Saturday, June 16, 2012 Here are a number of approaches I’ve seen applications take. I suppose you can see which one I prefer. :) The first option is to make all your application events into data events. You can do this by putting all your shared state on a model instance - you can make a model that doesn’t fetch from an API endpoint - and then within your views attach behavior to data-change events on that model. You can attach this ‘centralized state’ model to your top-level application object, so all your views can access it. This might work for some applications. As the app gets bigger, though, this piece of centralized state becomes a grab bag of data, and inevitably, some behavior. You need to store everything in it, even something only two subviews (out of potentially hundreds) care about. The next pattern I’ve seen in some apps is to give each view handles to the other views that it needs to notify. Often apps start out this way - it’s natural, I think, to look at the first instance of the app-level-event problem and think, “well, I’ll just make sure view A has a variable that is view B, and it’ll just notify view B when the event happens.” With more than, say, one event, though, this gets messy. Every view needs to, at the very least, keep a list of all the other views that should be notified when the event occurs. You can prevent them from having to keep references by implementing an app-level view lookup, but that doesn’t really solve the problem. When you add a new view that is interested in a particular event, you don’t want to have to remember to modify the view that generates that event (or the views, plural, that generate it, as is usually the case). So the third option is to have the top-level view on each page mediate events of its subviews, which is often how events are handled in the page-class phase of JavaScript. This has the same drawback as the previous option, though, which is that any time you change how the events are handled in the subviews, you have to modify the parent view as well.
  81. var app = { notifiers: {}, subscribe: function(eventName, handler) {

    notifiers[eventName] = notifiers[eventName] || []; notifiers[eventName].push(handler); } }; Subscribe 81 Saturday, June 16, 2012 So here’s what the subscribe function looks like. There’s a list of notifiers that is empty at first, and there’s a subscribe function that’s accessible to all the views and sub-views. When a view calls subscribe, they pass in two things: the name of the event they are interested in, and a function that should be called when the event occurs. The subscribe method adds that handler to the list for that event. That’s it for subscribe! You may be wondering, though...how did I get this app object? How does it make subscribe available to all the views? We’re going to talk about that next, so hang on.
  82. Subscribe initialize: function() { var self = this; app.subscribe("log-in", function()

    { self.render(); }); } 82 Saturday, June 16, 2012 Here’s what it looks like to call the subscribe function, from within a view. Let’s say we had a nav bar view that wanted to be notified whenever the user completed logging in. That view’s initialize would look like this. It calls app.subscribe, gives it the name of the event, and a function to call when that event happens. Some apps end up implementing a top-level declarative system for these events, similar to the events object that captures DOM events. This cleans up your initialize methods quite a bit. :) Let’s take a look at publish - the other side of this operation.
  83. var app = { /* other stuff omitted */ publish:

    function(eventName, data){ handlers = notifiers[eventName] || []; handlers.each(function(handler){ handler(data); }); } } Publish 83 Saturday, June 16, 2012 So here is what a view calls when it wants to publish an event. The first parameter is the event name, same as it was with subscribe. The second parameter is any data that should be passed to the views that are being notified. In the case of user log in, this data might be the name and ID of the user who successfully authenticated. Within publish, first, we get the list of handlers that have subscribed to this event. Now you’ll notice that this means we need the publishers and subscribers to agree on event names. If the app gets big enough, you can solve this with an object whose only job it is to hold constants that signify various events. For now strings are probably sufficient. Then it goes through the list, which is a list of functions, and calls each one, passing it the data that the view gave it when it published the event.
  84. Publish onSuccessfulLogIn: function(userData) { /* do other stuff */ app.publish("log-in",

    userData); } 84 Saturday, June 16, 2012 And here’s what it looks like when a view publishes an event. onSuccessfulLogIn is a function that is called when an ajax request to authenticate returns 200. It then publishes an event called “log-in” and passes along the data that came back from the server. So that’s publish and subscribe - and to be clear, this is something that you need to add to backbone. It doesn’t have this capacity to handle application-level events by default. Coming back to that app object...where do you add these publish and subscribe functions so that all your views can access them?
  85. Our Progress So Far 1. Get data from the server

    2. Render HTML elements 3. Handle events 4. Set up the app ✓ ✓ AKA: models & collections views & templates um, events what do I put in document.ready? Task: ✓ 85 Saturday, June 16, 2012 Well, that’s the last part of backbone basics - setting up the overall application structure.
  86. Image credit: http://www.flickr.com/photos/ed_gaillard/4171223632 Putting it all together 86 Saturday, June

    16, 2012
  87. Starting the app var app = { initialize: function() {

    var mainMessages = new Messages; mainMessages.fetch(); var this.mainView = new MessagesView({ collection: mainMessages }); }, /* other stuff: publish/subscribe etc. */ }; 87 Saturday, June 16, 2012 Most client-side JavaScript applications will have some kind of top-level ‘app’ object. You can call it whatever you want - many apps call it the name of the app, whatever that is, and many just use the name ‘app’ because it’s shorter. It’s a plain old JavaScript object - not a backbone object. This is another thing that backbone has no facility for. Remember, it’s lightweight. And why do we need the app object? Well, for one thing, it gives you a centralized place to put your publish and subscribe methods. Since it’s in the top-level namespace, you can access it from anywhere in your backbone app. In addition, you need one easy thing to call from your document-ready that kicks the whole application off. So you can also put an initialize function on this app object, which has the code that gets the app going, and then just call that function from your HTML page. Here’s what initialize looks like. I hope by now you’re starting to hate this code, because we’ve seen it on what feels like most of my slides. Just in case you’ve been under a rock, this is the code that creates a new messages collection, calls fetch to get the list messages from the server, and then passes that collection into a new MessagesView. If you remember, creating the messages view actually renders the HTML for the message list, so this is everything we need to get the app started.
  88. $(function() { app.initialize(); }); Starting the app 88 Saturday, June

    16, 2012 Once you have that, the only thing you need to do in your HTML is load all your JavaScript files and then call the initialize function. And that’s all there is to it!
  89. Our Progress So Far 1. Get data from the server

    2. Render HTML elements 3. Handle events 4. Set up the app ✓ ✓ AKA: models & collections views & templates um, events what do I put in document.ready? Task: ✓ ✓ 89 Saturday, June 16, 2012 So we’ve gone through the steps of setting up a basic backbone.js application.
  90. Agenda •Why you need it •Models •Views •Testing •Real-world patterns

    •Templates •Events •Starting the app •Other frameworks •Questions 1:45 - 2:30 (45 min) Basics One 2:30 Short Break 2:35 - 3:15 (40 min) Basics Two 3:15 Coffee Break! 3:45 - 4:25 (40 min) Beyond One 4:25 Short Break 4:30 - 5:15 (45 min) Beyond Two 90 Saturday, June 16, 2012 That brings us to our second break. This break features...
  91. Image credit: http://www.flickr.com/photos/fortherock/3899151934 91 Saturday, June 16, 2012 baby pandas!!

  92. 92 Saturday, June 16, 2012 Cutest. GIF. Ever.

  93. Agenda •Why you need it •Models •Views •Testing •Real-world patterns

    •Templates •Events •Starting the app •Other frameworks •Questions 1:45 - 2:30 (45 min) Basics One 2:30 Short Break 2:35 - 3:15 (40 min) Basics Two 3:15 Coffee Break! 3:45 - 4:25 (40 min) Beyond One 4:25 Short Break 4:30 - 5:15 (45 min) Beyond Two 93 Saturday, June 16, 2012 Also, coffee. Hooray for coffee. Let’s going into the ‘beyond’ section of this workshop.
  94. Testing 94 Saturday, June 16, 2012 JavaScript testing. It’s large.

    It’s scary. Lots of people talk about it. Almost nobody does it. Who here writes JavaScript unit tests? Who here tests their JavaScript via Selenium or some other type of acceptance testing? And who tests it by changing the code and hitting reload?
  95. Testing • Pivotal uses Jasmine http://pivotal.github.com/jasmine • Unit testing >

    integration testing • Go see Davis’s talk tomorrow! 1:45pm in continental 5 95 Saturday, June 16, 2012 Before I worked at Pivotal, my JavaScript testing consisted mostly of hitting reload to see if my changes worked. Pivotal wrote an open source JavaScript unit testing framework called Jasmine, originally because we were doing WebOS development, and we needed a test framework that didn’t depend on a DOM. These days, WebOS being somewhat over, we mostly use it to unit-test JavaScript on web applications. In general, we prefer to unit-test our JavaScript, rather than integration-test it through something like Selenium. Mostly this is because full-stack tests are very slow compared to unit tests, but also, unit tests give you more specific, more useful feedback when they fail. I much prefer to catch a regression in a unit test than in an integration test. For lots more info on Jasmine, I recommend Davis Frank’s Fluent talk. You can find more resources from Jasmine’s homepage - http://pivotal.github.com/jasmine
  96. Testing describe("Messages", function(){ it("has the right model", function(){ var messages

    = Messages.new expect(messages.model).toEqual(Message) }); }); 96 Saturday, June 16, 2012 Here’s what a unit test for the Messages collection looks like. Our Messages collection doesn’t have much behavior, but we did tell it that its model should be a Message model. So, we have a test in here that we remembered to do that. The syntax is Behavior-Driven-Development (BDD) style. Ruby folks will note that the “it” blocks nested with “describe” blocks look a lot like rspec. Expectations, such as on line 4, mostly use the “expect” function with various matchers such as toEqual, toBeGreaterThan, with modifiers such as “not” - so you can do something like: expect (messages.model).not.toEqual(User)
  97. Testing describe("MessagesView", function(){ it("renders on init", function() { expect($(".messages-container")) .not.toExist();

    new MessagesView({}); expect($(".messages-container")) .toExist(); }); }); 97 Saturday, June 16, 2012 Here’s a slightly more complex unit test, for our MessagesView. We’re testing that when you initialize a MessagesView, it renders its container into the DOM. We have a precondition that says the element with class ‘messages-container’ should not exist. Then we create a new MessagesView. Then, we have an expectation that says that now, an element with class “messages-container” should exist.
  98. Image credit: http://www.flickr.com/photos/jennyleesilver/101527048 98 Saturday, June 16, 2012 Here are

    a few gotchas when using Jasmine to test Backbone code.
  99. Mocking events var  MessagesView  =    Backbone.View.extend({    /*  other

     stuff  omitted  */    events:  {        "click  .time":  "showTooltip"      },    showTooltip:  function(){  /*  ...  */  }  }); 99 Saturday, June 16, 2012 Remember events? Well, one of the things you typically want to test is that the events are attached to the right behavior. Ideally, you’d trigger the event in the DOM and then assert that your function, in this case showTooltip, was called. Then you could unit-test showTooltip in isolation to make sure it is working properly. But when you set up a Backbone event like this, it does something weird to the showTooltip function. In Jasmine, if you set up a spy on the showTooltip method, which is supposed to tell you whether the function has been called, it just doesn’t work. Backbone is wrapping the function, or perhaps making a copy of the function...which it then calls when the event triggers. So backbone jasmine tests sometimes end up feeling a little integration-test-y, because to test this event, you have to trigger it in the DOM and then examine the DOM to see if the changes showTooltip was supposed to make have happened. In the Q&A session, one attendee recommended sinon.js for better mocking and stubbing, to get around these issues. I haven’t tried it on a project yet but I’m excited to see how it works.
  100. HTML Fixtures • jasmine-jquery gem === WIN • You do

    need some way to generate snippets of DOM from your server application. 100 Saturday, June 16, 2012 If you have an existing web application that you’re adding backbone to, it is very common to have a mix of server-side and client-side rendering. When markup is rendered server-side, sometimes after it goes down to the browser, a backbone view will take over some part of it. To effectively test these types of backbone views that take over server-side rendered markup, you need to be able to load a piece of markup into the jasmine test runner to operate on. Ideally, this markup should match what the server is rendering. It’s sometimes not entirely clear how you set this system up, so here’s the crash course. First, you need a way to generate snippets of DOM in your server application, and save them as files. In Rails apps, we typically do this during controller specs. We’ll render the controller action, then pick out a piece of the body from the response and save it to a file for jasmine to use later. If you have no automatic way to do this, you can just render the page into your browser, and then cut & paste the part of the DOM you need into a file. Second, you need some way for your jasmine test runner to load those files. Jasmine doesn’t do this out of the box, because it was originally designed for testing webOS, which didn’t have a DOM. This is where the jasmine-jquery gem comes in. It provides two unrelated things: jQuery- specific jasmine matchers (useful in their own right), PLUS a way to load fixtures from HTML files when you’re running your specs. The jasmine-jquery docs have more info as to where it expects the files to be and other details - see the homepage at http://github.com/velesin/ jasmine-jquery
  101. More ? Go see Davis’s talk tomorrow at 1:45 in

    Continental Ballroom 5 Image credits: Pivotal Labs 101 Saturday, June 16, 2012 And if you’re asking yourself, how can I get more jasmine in my life? The answer is to go see Davis’s talk tomorrow at 1:45 in continental ballroom 5. And uh, he says my check is in the mail. So that’s what I have to say about testing, at least for now. We’ve covered the basics of building an app, testing it, and setting it up. Now we’re going to look at some large-scale patterns for real-world Backbone development in webapps.
  102. Patterns Image credit: http://www.flickr.com/photos/mirjana/21299050 102 Saturday, June 16, 2012 We’re

    going to finish out this section looking at some common patterns for organizing code in a backbone app. First up: models.
  103. Photo credit: http://www.flickr.com/photos/mirjana/21299050 Model patterns 103 Saturday, June 16, 2012

  104. Server Server models: Browser Backbone models: internet cloud thingy Message

    User Friendship Message Friend User CurrentUser /current_user /users /friends /messages 104 Saturday, June 16, 2012 Remember this diagram? We talked about this as a good pattern for model organization, and we talked about how our backbone models mirror our RESTful resources. Now let’s break this down a little bit. It works automatically as long as those resource API endpoints map easily and naturally to the different objects you want to work with in your views. In the real world, sometimes that’s not the case. A lot of APIs aren’t exactly RESTful. A lot of APIs follow some other convention entirely. It’s possible to work with all of these in backbone - even APIs that return XML. (That’s a joke - Ruby folks generally prefer json :)
  105. Messy API Options 1. Backbone models hide persistence logic 2.

    Intermediary app hides persistence logic 3. Backbone model set reflects the messy API X ✓ ✓ 105 Saturday, June 16, 2012 So when I’m thinking about how I map my models to my APIs, I try to create models that encapsulate the ideas I’m working at the user interface level. That’s important, because it lets me hide all the messiness of the API(s) in the model layer. That way my views can just deal with data and events. The downside of this approach is that I sometimes end up rolling my own persistence and syncing code in JavaScript, if my API endpoints aren’t exactly what I need. Another thing I’ve done in that situation, which I personally like better, is throw up a little server app with the RESTful API that I want for my models. Then that app has all the messy code that figures out how to get and persist data. As a result, my backbone models can be mirror the resources of my intermediary app, and be relatively clean. What you don’t want to do is create a set of models that reflect too closely the chaos of the API layer. Think UI objects. And then pick one of the first two options. Which one you choose is probably going to be based on how comfortable you are with server-side development vs. JavaScript development. I’ve noticed people like me, who come from the back-end development world, tend to prefer an intermediary app so that our JavaScript can stay (relatively) simple. People coming from the front-end development world tend to be more comfortable with having that complexity in JavaScript. So it depends a lot on the makeup of your team and their relative comfort levels with the different technologies. Just don’t go for #3. :p So that’s model organization. Let’s talk about view organization, which we didn’t actually touch on at all while we were building our little app.
  106. Photo credit: http://www.flickr.com/photos/quasimondo/3209469713 View patterns 106 Saturday, June 16, 2012

    View patterns!
  107. View Organization MessagesView ProfileView MyFriendsView app app.currentView 107 Saturday, June

    16, 2012 When I talk about view organization, I think of 2 things: how we swap active views in and out in response to user input, and how we handle nested views. Let’s talk first about view swapping. If you think back to the verrry first session of this workshop, we talked about the evolution of JavaScript in web apps. And the phase right before frameworks was page classes. I like to use a pattern for active view management in backbone that pretty strongly resembles page classes. Now, this works if your app actually has different conceptual pages. In this pattern the application object, which you remember was a plain old JavaScript object - not a backbone object - knows what the current view being shown is. It keeps a handle to the current view. The app then subscribes to a pageChange event or set of events, which can be published by any subview. When it catches one of those, it figures out how to show the new view - either by pulling it out of a cache, or new’ing one up. This can get complicated, because the app object has to know all about what events should prompt changing to which view.
  108. Routers var  routes  =    Backbone.Router.extend({    routes:  {  

         "profile":  "showProfile",          "messages":  "showMessages"      },    showProfile:  function(){  /*  ...  */  }  }); 108 Saturday, June 16, 2012 So it turns out that backbone actually does give you some facility for page swapping in its Routers module. It also, handily, has a way for you to change the URL of the page from the router as well, using pushState, and falling back to hashtags on browsers that don’t support it. It starts out looking like this - a router has a routes declaration, sort of like views have events. The keys in this object are strings that have a URL fragment. For the first one, think slash-profile, or hash-profile if the browser doesn’t support pushState. When the router sees that URL come up, it will call the function showProfile. That function can pull a ProfileView from a cache, or new one up, and remove the old active view. So...how does that URL come up?
  109. Routers app.routes.navigate("profile"); 109 Saturday, June 16, 2012 Well, in your

    view, you can do this to invoke that router method, maybe in response to a click. And you can pass data here as part of the URL, and the function in the router that handles profile will get that data. So with this pattern, rather than using an app-level event system to handle switching the active view, we’re letting the router handle the logic of what view should be shown when. I think I’ve said this a thousand times so far today but here it is again: backbone is flexible. If you want to use the router, awesome. If you want to handle it yourself via events, go for it. If you want to have every event in every subview do the instantiation of the new view, sure! That last pattern is, er, not recommended.
  110. Subviews 110 Saturday, June 16, 2012 The last bit about

    view organization is subviews. Like event handling, different applications diverge dramatically in how they deal with subviews. The basic problem is that if you have a view that has subviews within it, how and when does the parent view tell the children views to render? In our little application, we just rendered the entire messages view in the parent view’s render method, rather than creating sub view objects for each of the messages. That’s actually a pretty common pattern, especially as your subviews are undergoing design revisions. Once you start feeling like you need a single message outside the main view, like in a modal when you click ‘details’ or something, then I would think about making it a subview and just iterating over them in the parent view’s render, and calling render on each one. But here we run into the desire to minimize touching the DOM. So it depends on what your constraints are. If you’re changing rapidly, do it all in one place. If you’re going for performance, collect elements from the subviews and insert them all at once. There is, sadly, no silver bullet and no one-size-fits-all solution.
  111. Agenda •Why you need it •Models •Views •Testing •Real-world patterns

    •Templates •Events •Starting the app •Other frameworks •Questions 1:45 - 2:30 (45 min) Basics One 2:30 Short Break 2:35 - 3:15 (40 min) Basics Two 3:15 Coffee Break! 3:45 - 4:25 (40 min) Beyond One 4:25 Short Break 4:30 - 5:15 (45 min) Beyond Two 111 Saturday, June 16, 2012 Ok, we’ve reached our 4:25 break. I thought for this one we could relax with some nice scenery.
  112. Photo credit: http://www.flickr.com/photos/vastateparksstaff 112 Saturday, June 16, 2012

  113. Photo credit: http://www.flickr.com/photos/blmiers2/6814372181 113 Saturday, June 16, 2012

  114. Agenda •Why you need it •Models •Views •Testing •Real-world patterns

    •Templates •Events •Starting the app •Other frameworks •Questions 1:45 - 2:30 (45 min) Basics One 2:30 Short Break 2:35 - 3:15 (40 min) Basics Two 3:15 Coffee Break! 3:45 - 4:25 (40 min) Beyond One 4:25 Short Break 4:30 - 5:15 (45 min) Beyond Two 114 Saturday, June 16, 2012 Ok, we are (finally) at the last session. Thanks for hanging in there this whole time. To finish up I’d like to talk a little bit about where I see backbone in the constellation of JavaScript frameworks. You might have thought I’d put this first, to set the stage, but I think it’s actually most useful once you have some concrete knowledge of what Backbone does and how it does it.
  115. Backbone.js Cappucino Ext.js AngularJS Knockback.js Agility.js Sammy.js Knockout Spine.js JavaScriptMVC

    Ember.js Sproutcore Google Web Toolkit Batman.js Google Closure Meteor YUI troopJS olives dijon Fidel 115 Saturday, June 16, 2012 Remember this slide? When we looked at this slide the first time through, I said that these frameworks each address a slightly different set of problems, but all fundamentally have the same goal: to make writing large JavaScript applications easier. But there are lots of different kinds of “large JavaScript applications.” Some are desktop apps or mobile apps - they need native-platform-looking UI widgets. Some live in a browser - they need browser-native-looking UI widgets, but because of what’s available in the browser for free, they have less need for UI widgets in general. Some live in a browser but want to feel like desktop apps. Some depend heavily on APIs, others just use local storage and the occasional call out. In addition to differing in what technical problems they’re addressing, these frameworks also address different social problems within a development team. Now they may not list this stuff on their features page, but it is just as important to know what social problems they’re addressing as it is to be able to rattle off their technical differences. Some of these are designed to make it so you can write JavaScript without having to actually write JavaScript. This is great for a team with little JavaScript experience, because JavaScript is tricky compared to what most people have experience with. Having static type tracking to catch bugs can also be good for a team with a lot of new programmers. Personally I think you can solve that problem better by pair programming, more communication, and writing more and more clear tests. Some of these provide a lot of structure, are ‘opinionated’, as they say. With those you give up flexibility in exchange for velocity. Whether you want to make that tradeoff depends on the project and the team you have.
  116. Backbone is good at... 1. Interacting heavily with a RESTful

    API 2. Living in harmony with other frameworks and styles of JavaScript 3. Handling special snowflake applications 116 Saturday, June 16, 2012 So here’s what backbone is good at. 1. It’s good at mirroring data from a RESTful API. There is still some manual work involved to save at the right time, etc., but all in all, backbone does a pretty good job with this. 2. It’s especially good at working with an application that already has server-side rendering and some JavaScript - an app that is now transitioning towards a more client-side-heavy user experience. This is where Backbone’s flexibility and lightweight nature are really an advantage. 3. If you’ve got a weird setup - nonRESTful APIs, an existing eventing system you want to use, etc. backbone is great at accomodating that. Because it is not opinionated, you almost never run in to ‘features’ of the system that hold you back from doing what you want.
  117. It’s not so good at... 1. Cool UI widgets 2.

    Data binding across views 3. Giving structure to beginner JS developers 117 Saturday, June 16, 2012 And here’s what backbone isn’t so awesome at. 1. First of all, it’s not a UI widget library. Some folks think “javascript library” is synonymous with “fancy ui widgets” but in this case they’re totally unrelated. Backbone is a plumbing library. 2. The second thing it’s not great at is data binding across views. What I mean by that is that if you have a model representing a piece of data on your server, there’s no built-in facility for automatically re-rendering the views it is attached to when it changes. This is typically something you build in yourself. In other frameworks, such as ember.js, this is part of the framework. 3. And finally, it’s not great at giving structure to beginner JS developers. It’s quite common to get a team for a big clientside JavaScript application that includes mostly people with a little, or no, JavaScript experience. If you’re a senior developer on that team, you might want to think about using a framework that gives your other developers a little more structure. With backbone’s flexibility, it’s pretty easy to have folks work on different parts of the code on their own, and then when you review it, discover that they all did even the basic stuff very differently. When you’re having an issue in backbone and you google for other people’s approaches, there’s no ‘right answer.’ For any given topic, you’ll find a couple dozen blog posts, all with different assertions about which way you should approach it.
  118. In general 1. Backbone works fine on a greenfield application...but

    consider alternatives. 2. Backbone’s sweet spot is when you’re converting an existing web app to have more client side behavior. 118 Saturday, June 16, 2012 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.
  119. Thank you! Questions? Sarah Mei @sarahmei sarah@pivotallabs.com 119 Saturday, June

    16, 2012