Backbone.Js on Rails
Total Page:16
File Type:pdf, Size:1020Kb
1 Build snappier, more interactive apps with cleaner code and better tests in less time Backbone.js on Rails thoughtbot Jason Morrison Chad Pytel Nick Quaranto Harold Giménez Joshua Clayton Gabe Berke-Williams Chad Mazzola May 24, 2013 Contents 1 Introduction 7 The shift to client-side web applications ................. 7 Goals for this book ............................ 9 Alternatives to Backbone ......................... 9 The example application ......................... 10 Framework and library choice in the example application .... 10 Conventions used in this book ...................... 11 2 Getting up to speed 12 Backbone online resources ....................... 12 JavaScript resources ........................... 13 3 Organization 14 Backbone and MVC ............................ 14 What goes where ............................. 15 Namespacing your application ...................... 17 Mixins ................................... 18 1 CONTENTS 2 4 Rails Integration 20 Organizing your Backbone code in a Rails app ............. 20 Rails 3.0 and prior ............................. 20 Jammit and a JST naming gotcha ................. 22 Rails 3.1 and above ............................ 23 An overview of the stack: connecting Rails and Backbone ....... 25 Setting up models .......................... 26 Setting up Rails controllers ..................... 27 Setting Up Views .......................... 30 Customizing your Rails-generated JSON ................ 32 ActiveRecord::Base.include_root_in_json ............. 34 Converting an existing page/view area to use Backbone ........ 36 Breaking out the TaskView ..................... 39 Using Rails URL helpers from Backbone ................. 43 5 Routers, Views, and Templates 47 View explanation ............................. 47 Initialization ............................. 48 The View’s element ......................... 49 Customizing the View’s Element .................. 49 Rendering .............................. 50 Events ................................ 51 Templating strategy ............................ 51 Choosing a strategy ............................ 53 Adding Backbone to existing Rails views ............. 54 Writing new Backbone functionality from scratch ......... 54 CONTENTS 3 Routers .................................. 56 Example router ........................... 57 The routes hash ........................... 58 Initializing a router .......................... 59 Event binding ............................... 59 Binding to DOM events within the view element ......... 60 Events observed by your view ................... 62 Events your view publishes ..................... 63 Cleaning up: unbinding .......................... 66 Why unbind events? ......................... 66 Unbinding DOM events ....................... 70 Backbone’s listentTo and stopListening .............. 70 Unbinding model and collection events .............. 71 Keep track of on() calls to unbind more easily ........... 72 Unbinding view-triggered events .................. 73 Establish a convention for consistent and correct unbinding ... 74 Swapping router ............................. 74 SwappingRouter and Backbone internals ............. 76 Composite views ............................. 79 Refactoring from a large view .................... 79 Cleaning up views properly ..................... 88 Forms ................................... 88 Building markup ........................... 89 Serializing forms ........................... 90 A Backbone forms library ...................... 91 Display server errors ........................ 91 Internationalization ............................ 94 CONTENTS 4 6 Models and collections 96 Filters and sorting ............................. 96 Modifying collections in-place ...................... 96 Filtering .................................. 97 Propagating collection changes ..................... 100 A note on event bindings and reference leaks .......... 101 Sorting ................................... 103 Validations ................................. 107 Model relationships ............................ 114 Backbone-relational plugin ........................ 115 Relations in the task app ......................... 115 Deciding how to deliver data to the client ................ 115 Designing the HTTP JSON API ...................... 116 Implementing the API: presenting the JSON ............... 118 Parsing the JSON and instantiating client-side models ......... 119 When to fetch deferred data ....................... 121 Complex nested models ......................... 122 Composite models ............................ 122 accepts_nested_attributes_for ...................... 123 Example for accepts_nested_attributes_for ............... 127 Duplicating business logic across the client and server ......... 127 An example: model validations ................... 127 Kinds of logic you duplicate .................... 130 Validations .............................. 131 Querying ............................... 132 Callbacks .............................. 132 CONTENTS 5 Algorithms .............................. 132 Synchronizing between clients ...................... 133 The moving parts .......................... 134 Putting it together: A look at the life cycle of a change ...... 134 Implementation: Step 1, Faye server ................ 136 Implementing it: Step 2, ActiveRecord observers ......... 137 Implementing it: Step 3, In-browser subscribers .......... 140 Testing synchronization ....................... 142 Further reading ........................... 144 Uploading attachments .......................... 145 Saving files along with attributes .................. 145 Separating file upload and model persistence ........... 146 Example, Step 1: Upload interface ................. 146 Example, Step 2: Accept and persist uploads in Rails ....... 150 Example, Step 3: Display Uploaded Files ............. 151 7 Testing 156 Full-stack integration testing ....................... 156 Introduction ............................. 156 Capybara .............................. 157 Cucumber .............................. 158 Drivers ................................ 159 Isolated unit testing ............................ 160 Isolation testing in JavaScript .................... 161 What to test? ............................ 161 Helpful Tools ............................. 163 CONTENTS 6 Example: Test-driving a task application ................. 163 Setup ................................ 163 Step by step ............................. 164 8 Security 178 Encoding data when bootstrapping JSON data ............. 178 9 Application Structure 180 Structuring larger Backbone applications ................ 180 Chapter 1 Introduction The shift to client-side web applications Modern web applications have become increasingly rich, shifting their com- plexity onto the client side. While there are very well-understood approaches embodied in mature frameworks to organize server-side code, frameworks for organizing your client-side code are newer and generally still emerging. Back- bone is one such library that provides a set of structures to help you organize your JavaScript code. Libraries like jQuery have done a great deal to help abstract inconsistencies across browsers and provide a high-level API for making AJAX requests and performing DOM manipulation, but larger and richer client-side applications that lack decoupled and modular organizational structures often fall victim to the same few kinds of technical debt. These apps are often highly asynchronous and the “path of least resistance” implementation is often to have deeply nested callbacks to describe asyn- chronous behavior, with nested Ajax calls and success/failure conditional concerns going several layers deep. Rich client-side applications almost always involve a layer of state and logic on the client side. One way to implement this is to store domain objects or busi- ness logic state in the DOM. However, storing state in the DOM, stashing your 7 CHAPTER 1. INTRODUCTION 8 application’s data in hidden <div> elements that you clone, graft, and toggle into and out of view, or reading and writing to lengthy sets of HTML data-* attributes can quickly get cumbersome and confusing. A third common feature in rich client-side apps is the presentation of multiple views on a single domain object. Consider a web conferencing application with multiple views on the members of your contact list - each contact is rendered in brief inside a list view, and in more specificity in a detail view. Additionally, your conference call history includes information about the people who participated. Each time an individual contact’s information changes, this information needs to cascade to all the view representations. This often leads to a tight coupling of persistence and presentation: invoking $.ajax to save a user’s update and then updating several specific DOM ele- ments upon success. By separating business logic, persistence, and presentation concerns, and pro- viding a decoupled, event-driven way to cascade changes through a system of observers, each module of code is more well-encapsulated and expresses a cohesive set of responsibilities without being coupled to outside concerns. Your application code becomes easier to test, modify, and extend, and you can better manage its complexity while its feature set grows. Granted, you can thoughtfully organize your code in a clean, coherent man- ner without using an external library. However, using a library like Backbone helps you get started