From 6a0ac930863ab4f1cf107d879aa658495372cb2b Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Thu, 2 Dec 2010 09:31:55 -0500 Subject: [PATCH 001/167] Fixing Issue #109 -- ignore 'add' and 'remove' events that originate from models shared with other collections. --- backbone.js | 6 ++++-- test/collection.js | 27 ++++++++++++++++++--------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/backbone.js b/backbone.js index 04fe46ea6..640fbb83e 100644 --- a/backbone.js +++ b/backbone.js @@ -564,8 +564,10 @@ // Internal method called every time a model in the set fires an event. // Sets need to update their indexes when models change ids. All other - // events simply proxy through. - _onModelEvent : function(ev, model) { + // events simply proxy through. "add" and "remove" events that originate + // in other collections are ignored. + _onModelEvent : function(ev, model, collection) { + if ((ev == 'add' || ev == 'remove') && collection != this) return; if (ev === 'change:id') { delete this._byId[model.previous('id')]; this._byId[model.id] = model; diff --git a/test/collection.js b/test/collection.js index 4fccb8d81..3f2f84be8 100644 --- a/test/collection.js +++ b/test/collection.js @@ -8,12 +8,13 @@ $(document).ready(function() { lastRequest = _.toArray(arguments); }; - var a = new Backbone.Model({id: 3, label: 'a'}); - var b = new Backbone.Model({id: 2, label: 'b'}); - var c = new Backbone.Model({id: 1, label: 'c'}); - var d = new Backbone.Model({id: 0, label: 'd'}); - var e = null; - var col = window.col = new Backbone.Collection([a,b,c,d]); + var a = new Backbone.Model({id: 3, label: 'a'}); + var b = new Backbone.Model({id: 2, label: 'b'}); + var c = new Backbone.Model({id: 1, label: 'c'}); + var d = new Backbone.Model({id: 0, label: 'd'}); + var e = null; + var col = new Backbone.Collection([a,b,c,d]); + var otherCol = new Backbone.Collection(); test("Collection: new and sort", function() { equals(col.first(), a, "a should be first"); @@ -53,26 +54,34 @@ $(document).ready(function() { }); test("Collection: add", function() { - var added = opts = null; + var added = opts = secondAdded = null; + e = new Backbone.Model({id: 10, label : 'e'}); + otherCol.add(e); + otherCol.bind('add', function() { + secondAdded = true; + }); col.bind('add', function(model, collection, options){ added = model.get('label'); opts = options; }); - e = new Backbone.Model({id: 10, label : 'e'}); col.add(e, {amazing: true}); equals(added, 'e'); equals(col.length, 5); equals(col.last(), e); + equals(otherCol.length, 1); + equals(secondAdded, null); ok(opts.amazing); }); test("Collection: remove", function() { - var removed = null; + var removed = otherRemoved = null; col.bind('remove', function(model){ removed = model.get('label'); }); + otherCol.bind('remove', function(){ otherRemoved = true; }); col.remove(e); equals(removed, 'e'); equals(col.length, 4); equals(col.first(), d); + equals(otherRemoved, null); }); test("Collection: remove in multiple collections", function() { From d4dc736a82254d7bbc58789733832a91a102d5cf Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Thu, 2 Dec 2010 09:47:12 -0500 Subject: [PATCH 002/167] adding a bit more of a test for changedattributes. --- test/model.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/model.js b/test/model.js index eb3efaed9..c1dfe3e99 100644 --- a/test/model.js +++ b/test/model.js @@ -150,6 +150,7 @@ $(document).ready(function() { test("Model: change, hasChanged, changedAttributes, previous, previousAttributes", function() { var model = new Backbone.Model({name : "Tim", age : 10}); + equals(model.changedAttributes(), false); model.bind('change', function() { ok(model.hasChanged('name'), 'name changed'); ok(!model.hasChanged('age'), 'age did not'); From b085fa0099d75cf112e07ac580f282fb22afa7b9 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Thu, 2 Dec 2010 09:59:26 -0500 Subject: [PATCH 003/167] Events#trigger ... making it safe to unbind your own event within a trigger() call. --- backbone.js | 6 ++++-- test/events.js | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/backbone.js b/backbone.js index 640fbb83e..3031eb106 100644 --- a/backbone.js +++ b/backbone.js @@ -92,12 +92,14 @@ trigger : function(ev) { var list, calls, i, l; if (!(calls = this._callbacks)) return this; - if (list = calls[ev]) { + if (calls[ev]) { + list = calls[ev].slice(0); for (i = 0, l = list.length; i < l; i++) { list[i].apply(this, Array.prototype.slice.call(arguments, 1)); } } - if (list = calls['all']) { + if (calls['all']) { + list = calls['all'].slice(0); for (i = 0, l = list.length; i < l; i++) { list[i].apply(this, arguments); } diff --git a/test/events.js b/test/events.js index 838084289..cdc8e8dfe 100644 --- a/test/events.js +++ b/test/events.js @@ -39,4 +39,18 @@ $(document).ready(function() { equals(obj.counterB, 2, 'counterB should have been incremented twice.'); }); + test("Events: two binds that unbind themeselves", function() { + var obj = { counterA: 0, counterB: 0 }; + _.extend(obj,Backbone.Events); + var incrA = function(){ obj.counterA += 1; obj.unbind('event', incrA); }; + var incrB = function(){ obj.counterB += 1; obj.unbind('event', incrB); }; + obj.bind('event', incrA); + obj.bind('event', incrB); + obj.trigger('event'); + obj.trigger('event'); + obj.trigger('event'); + equals(obj.counterA, 1, 'counterA should have only been incremented once.'); + equals(obj.counterB, 1, 'counterB should have only been incremented once.'); + }); + }); \ No newline at end of file From 524901083e9cb8e20aa7db844c117c9998f55e5a Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 2 Dec 2010 14:23:13 -0800 Subject: [PATCH 004/167] Add urlBase option to model to allow specifying restful url without using a collection --- backbone.js | 2 +- test/model.js | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/backbone.js b/backbone.js index 3031eb106..2663ff1a3 100644 --- a/backbone.js +++ b/backbone.js @@ -287,7 +287,7 @@ // using Backbone's restful methods, override this to change the endpoint // that will be called. url : function() { - var base = getUrl(this.collection); + var base = this.urlBase || getUrl(this.collection); if (this.isNew()) return base; return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + this.id; }, diff --git a/test/model.js b/test/model.js index c1dfe3e99..a7cdcdd80 100644 --- a/test/model.js +++ b/test/model.js @@ -65,6 +65,16 @@ $(document).ready(function() { equals(failed, true); doc.collection = collection; }); + + test("Model: url when using urlBase", function() { + var Model = Backbone.Model.extend({ + urlBase: '/collection' + }); + var model = new Model(); + equals(model.url(), '/collection'); + model.set({id: '1'}); + equals(model.url(), '/collection/1'); + }); test("Model: clone", function() { attrs = { 'foo': 1, 'bar': 2, 'baz': 3}; From bbcf19684c7fb87b78acfbcc33e12217265e08ae Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Fri, 3 Dec 2010 09:54:27 -0500 Subject: [PATCH 005/167] A silent change to a model will now make hasChanged() return true ... Issue #105 --- backbone.js | 10 ++++------ test/model.js | 2 ++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backbone.js b/backbone.js index 3031eb106..4217e6254 100644 --- a/backbone.js +++ b/backbone.js @@ -180,10 +180,8 @@ if (!_.isEqual(now[attr], val)) { now[attr] = val; delete escaped[attr]; - if (!options.silent) { - this._changed = true; - this.trigger('change:' + attr, this, val, options); - } + this._changed = true; + if (!options.silent) this.trigger('change:' + attr, this, val, options); } } @@ -206,8 +204,8 @@ // Remove the attribute. delete this.attributes[attr]; delete this._escapedAttributes[attr]; + this._changed = true; if (!options.silent) { - this._changed = true; this.trigger('change:' + attr, this, void 0, options); this.change(options); } @@ -227,8 +225,8 @@ this.attributes = {}; this._escapedAttributes = {}; + this._changed = true; if (!options.silent) { - this._changed = true; for (attr in old) { this.trigger('change:' + attr, this, void 0, options); } diff --git a/test/model.js b/test/model.js index c1dfe3e99..87e12e6ed 100644 --- a/test/model.js +++ b/test/model.js @@ -159,6 +159,8 @@ $(document).ready(function() { ok(_.isEqual(model.previousAttributes(), {name : "Tim", age : 10}), 'previousAttributes is correct'); }); model.set({name : 'Rob'}, {silent : true}); + equals(model.hasChanged(), true); + equals(model.hasChanged('name'), true); model.change(); equals(model.get('name'), 'Rob'); }); From 3623916f2b251d52394e1afc6f2fd24e49d9d7c6 Mon Sep 17 00:00:00 2001 From: Chris Korhonen Date: Fri, 3 Dec 2010 10:39:06 -0500 Subject: [PATCH 006/167] Added append option when fetching a collection. Useful when lazy loading data sets. --- backbone.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/backbone.js b/backbone.js index 12f8b8460..d124b942e 100644 --- a/backbone.js +++ b/backbone.js @@ -466,9 +466,13 @@ options || (options = {}); var collection = this; var success = function(resp) { - collection.refresh(collection.parse(resp)); - if (options.success) options.success(collection, resp); - }; + if(options.append) { + collection.add(collection.parse(resp)); + } else { + collection.refresh(collection.parse(resp)); + } + if (options.success) options.success(collection, resp); + }; var error = options.error && _.bind(options.error, null, collection); Backbone.sync('read', this, success, error); return this; From 3246463d3bbafda57e6b0bcdc3d8ac652354bab9 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Mon, 6 Dec 2010 11:32:30 -0500 Subject: [PATCH 007/167] Adding an FAQ section to the backbone docs. --- index.html | 161 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 136 insertions(+), 25 deletions(-) diff --git a/index.html b/index.html index 25a9462b3..df6000f33 100644 --- a/index.html +++ b/index.html @@ -263,6 +263,17 @@ Examples + + F.A.Q. + + + Change Log @@ -354,27 +365,6 @@

Introduction

— when the model changes, the views simply update themselves.

-

- How is this different than - SproutCore or - Cappuccino? - -

- -

- This question is frequently asked, and all three projects apply general - Model-View-Controller - principles to JavaScript applications. However, there isn't much basis - for comparison. SproutCore and Cappuccino provide rich UI widgets, vast - core libraries, and determine the structure of your HTML for you. - Both frameworks measure in the hundreds of kilobytes when packed and - gzipped, and megabytes of JavaScript, CSS, and images when loaded in the browser - — there's a lot of room underneath for libraries of a more moderate scope. - Backbone is a 4 kilobyte include that provides - just the core concepts of models, events, collections, views, controllers, - and persistence. -

-

Many of the examples that follow are runnable. Click the play button to execute them. @@ -550,16 +540,16 @@

Backbone.Model

Get the current value of an attribute from the model. For example: note.get("title")

- +

escapemodel.escape(attribute)
Similar to get, but returns the HTML-escaped version of a model's attribute. If you're interpolating data from the model into - HTML, using escape to retrieve attributes will prevent + HTML, using escape to retrieve attributes will prevent XSS attacks.

- +
 var hacker = new Backbone.Model({
   name: "<script>alert('xss')</script>"
@@ -1833,6 +1823,127 @@ 

Examples

+

F.A.Q.

+ +

+ Catalog of Events +
+ Here's a list of all of the built-in events that Backbone.js can fire. + You're also free to trigger your own events on Models and Views as you + see fit. +

+ +
    +
  • "add" (model, collection) — when a model is added to a collection.
  • +
  • "remove" (model, collection) — when a model is removed from a collection.
  • +
  • "refresh" (collection) — when the collection's entire contents have been replaced.
  • +
  • "change" (model, collection) — when a model's attributes have changed.
  • +
  • "change:[attribute]" (model, collection) — when a specific attribute has been updated.
  • +
  • "error" (model, collection) — when a model's validation fails, or a save call fails on the server.
  • +
  • "route:[name]" (controller) — when one of a controller's routes has matched.
  • +
  • "all" — this special event fires for any triggered event, passing the event name as the first argument.
  • +
+ +

+ Nested Models & Collections +
+ It's common to nest collections inside of models with Backbone. For example, + consider a Mailbox model that contains many Message models. + One nice pattern for handling this is have a this.messages collection + for each mailbox, enabling the lazy-loading of messages, when the mailbox + is first opened ... perhaps with MessageList views listening for + "add" and "remove" events. +

+ +
+var Mailbox = Backbone.Model.extend({
+
+  initialize: function() {
+    this.messages = new Messages;
+    this.messages.url = '/mailbox/' + this.id + '/messages';
+    this.messages.bind("refresh", this.updateCounts);
+  },
+
+  ...
+
+});
+
+var Inbox = new Mailbox;
+
+// And then, when the Inbox is opened:
+
+Inbox.messages.fetch();
+
+ +

+ How does Backbone relate to "traditional" MVC? +
+ Different implementations of the + Model-View-Controller + pattern tend to disagree about the definition of a controller. If it helps any, in + Backbone, the View class can also be thought of as a + kind of controller, dispatching events that originate from the UI, with + the HTML template serving as the true view. We call it a View because it + represents a logical chunk of UI, responsible for the contents of a single + DOM element. +

+ +

+ Binding "this" +
+ Perhaps the single most common JavaScript "gotcha" is the fact that when + you pass a function as a callback, it's value for this is lost. With + Backbone, when dealing with events and callbacks, + you'll often find it useful to rely on + _.bind and + _.bindAll + from Underscore.js. _.bind takes a function and an object to be + used as this, any time the function is called in the future. + _.bindAll takes an object and a list of method names: each method + in the list will be bound to the object, so that it's this may + not change. For example, in a View that listens for + changes to a collection... +

+ +
+var MessageList = Backbone.View.extend({
+
+  initialize: function() {
+    _.bindAll(this, "addMessage", "removeMessage", "render");
+
+    var messages = this.collection;
+    messages.bind("refresh", this.render);
+    messages.bind("add", this.addMessage);
+    messages.bind("remove", this.removeMessage);
+  }
+
+});
+
+// Later, in the app...
+
+Inbox.messages.add(newMessage);
+
+ +

+ + How is Backbone different than + SproutCore or + Cappuccino? + +
+ This question is frequently asked, and all three projects apply general + Model-View-Controller + principles to JavaScript applications. However, there isn't much basis + for comparison. SproutCore and Cappuccino provide rich UI widgets, vast + core libraries, and determine the structure of your HTML for you. + Both frameworks measure in the hundreds of kilobytes when packed and + gzipped, and megabytes of JavaScript, CSS, and images when loaded in the browser + — there's a lot of room underneath for libraries of a more moderate scope. + Backbone is a 4 kilobyte include that provides + just the core concepts of models, events, collections, views, controllers, + and persistence. +

+

Change Log

@@ -1841,7 +1952,7 @@

Change Log

jQuery, as a framework for DOM manipulation and Ajax support. Implemented Model#escape, to efficiently handle attributes intended for HTML interpolation. When trying to persist a model, - failed requests will now trigger an "error" event. The + failed requests will now trigger an "error" event. The ubiquitous options argument is now passed as the final argument to all "change" events.

From f194f2d53c0fd362b872573a922b1833c9f287cf Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Mon, 6 Dec 2010 11:33:42 -0500 Subject: [PATCH 008/167] adding an FAQ section. --- index.html | 161 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 136 insertions(+), 25 deletions(-) diff --git a/index.html b/index.html index 25a9462b3..df6000f33 100644 --- a/index.html +++ b/index.html @@ -263,6 +263,17 @@ Examples + + F.A.Q. + + + Change Log @@ -354,27 +365,6 @@

Introduction

— when the model changes, the views simply update themselves.

-

- How is this different than - SproutCore or - Cappuccino? - -

- -

- This question is frequently asked, and all three projects apply general - Model-View-Controller - principles to JavaScript applications. However, there isn't much basis - for comparison. SproutCore and Cappuccino provide rich UI widgets, vast - core libraries, and determine the structure of your HTML for you. - Both frameworks measure in the hundreds of kilobytes when packed and - gzipped, and megabytes of JavaScript, CSS, and images when loaded in the browser - — there's a lot of room underneath for libraries of a more moderate scope. - Backbone is a 4 kilobyte include that provides - just the core concepts of models, events, collections, views, controllers, - and persistence. -

-

Many of the examples that follow are runnable. Click the play button to execute them. @@ -550,16 +540,16 @@

Backbone.Model

Get the current value of an attribute from the model. For example: note.get("title")

- +

escapemodel.escape(attribute)
Similar to get, but returns the HTML-escaped version of a model's attribute. If you're interpolating data from the model into - HTML, using escape to retrieve attributes will prevent + HTML, using escape to retrieve attributes will prevent XSS attacks.

- +
 var hacker = new Backbone.Model({
   name: "<script>alert('xss')</script>"
@@ -1833,6 +1823,127 @@ 

Examples

+

F.A.Q.

+ +

+ Catalog of Events +
+ Here's a list of all of the built-in events that Backbone.js can fire. + You're also free to trigger your own events on Models and Views as you + see fit. +

+ +
    +
  • "add" (model, collection) — when a model is added to a collection.
  • +
  • "remove" (model, collection) — when a model is removed from a collection.
  • +
  • "refresh" (collection) — when the collection's entire contents have been replaced.
  • +
  • "change" (model, collection) — when a model's attributes have changed.
  • +
  • "change:[attribute]" (model, collection) — when a specific attribute has been updated.
  • +
  • "error" (model, collection) — when a model's validation fails, or a save call fails on the server.
  • +
  • "route:[name]" (controller) — when one of a controller's routes has matched.
  • +
  • "all" — this special event fires for any triggered event, passing the event name as the first argument.
  • +
+ +

+ Nested Models & Collections +
+ It's common to nest collections inside of models with Backbone. For example, + consider a Mailbox model that contains many Message models. + One nice pattern for handling this is have a this.messages collection + for each mailbox, enabling the lazy-loading of messages, when the mailbox + is first opened ... perhaps with MessageList views listening for + "add" and "remove" events. +

+ +
+var Mailbox = Backbone.Model.extend({
+
+  initialize: function() {
+    this.messages = new Messages;
+    this.messages.url = '/mailbox/' + this.id + '/messages';
+    this.messages.bind("refresh", this.updateCounts);
+  },
+
+  ...
+
+});
+
+var Inbox = new Mailbox;
+
+// And then, when the Inbox is opened:
+
+Inbox.messages.fetch();
+
+ +

+ How does Backbone relate to "traditional" MVC? +
+ Different implementations of the + Model-View-Controller + pattern tend to disagree about the definition of a controller. If it helps any, in + Backbone, the View class can also be thought of as a + kind of controller, dispatching events that originate from the UI, with + the HTML template serving as the true view. We call it a View because it + represents a logical chunk of UI, responsible for the contents of a single + DOM element. +

+ +

+ Binding "this" +
+ Perhaps the single most common JavaScript "gotcha" is the fact that when + you pass a function as a callback, it's value for this is lost. With + Backbone, when dealing with events and callbacks, + you'll often find it useful to rely on + _.bind and + _.bindAll + from Underscore.js. _.bind takes a function and an object to be + used as this, any time the function is called in the future. + _.bindAll takes an object and a list of method names: each method + in the list will be bound to the object, so that it's this may + not change. For example, in a View that listens for + changes to a collection... +

+ +
+var MessageList = Backbone.View.extend({
+
+  initialize: function() {
+    _.bindAll(this, "addMessage", "removeMessage", "render");
+
+    var messages = this.collection;
+    messages.bind("refresh", this.render);
+    messages.bind("add", this.addMessage);
+    messages.bind("remove", this.removeMessage);
+  }
+
+});
+
+// Later, in the app...
+
+Inbox.messages.add(newMessage);
+
+ +

+ + How is Backbone different than + SproutCore or + Cappuccino? + +
+ This question is frequently asked, and all three projects apply general + Model-View-Controller + principles to JavaScript applications. However, there isn't much basis + for comparison. SproutCore and Cappuccino provide rich UI widgets, vast + core libraries, and determine the structure of your HTML for you. + Both frameworks measure in the hundreds of kilobytes when packed and + gzipped, and megabytes of JavaScript, CSS, and images when loaded in the browser + — there's a lot of room underneath for libraries of a more moderate scope. + Backbone is a 4 kilobyte include that provides + just the core concepts of models, events, collections, views, controllers, + and persistence. +

+

Change Log

@@ -1841,7 +1952,7 @@

Change Log

jQuery, as a framework for DOM manipulation and Ajax support. Implemented Model#escape, to efficiently handle attributes intended for HTML interpolation. When trying to persist a model, - failed requests will now trigger an "error" event. The + failed requests will now trigger an "error" event. The ubiquitous options argument is now passed as the final argument to all "change" events.

From 21a3675db926831999096c3e368e1be5a78738a4 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Tue, 7 Dec 2010 02:39:39 -0500 Subject: [PATCH 009/167] Implement model.is() for attr test, with docs, tests --- backbone.js | 5 +++++ index.html | 8 ++++++++ test/model.js | 15 +++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/backbone.js b/backbone.js index 4217e6254..60f06d21f 100644 --- a/backbone.js +++ b/backbone.js @@ -158,6 +158,11 @@ return this._escapedAttributes[attr] = escapeHTML(val == null ? '' : val); }, + // Returns true if the attribute evalutates to a truthy value. False otherwise. + is : function(attr) { + return !!this.attributes[attr] === true; + }, + // Set a hash of model attributes on the object, firing `"change"` unless you // choose to silence it. set : function(attrs, options) { diff --git a/index.html b/index.html index df6000f33..b42423fe0 100644 --- a/index.html +++ b/index.html @@ -168,6 +168,7 @@
  • constructor / initialize
  • get
  • escape
  • +
  • is
  • set
  • unset
  • clear
  • @@ -558,6 +559,13 @@

    Backbone.Model

    alert(hacker.escape('name'));
    +

    + ismodel.is(attribute) +
    + Returns whether an attribute is set to a truthy value or not. For example: + note.get("title") +

    +

    setmodel.set(attributes, [options])
    diff --git a/test/model.js b/test/model.js index 87e12e6ed..00f53b499 100644 --- a/test/model.js +++ b/test/model.js @@ -104,6 +104,21 @@ $(document).ready(function() { equals(doc.escape('audience'), ''); }); + test("Model: is", function() { + attrs = { 'foo': 1 }; + a = new Backbone.Model(attrs); + // falsiness + _([false, null, undefined, '', 0]).each(function(value) { + a.set({'foo': value}); + equals(a.is("foo"), false); + }); + // truthiness + _([true, "Truth!", 1]).each(function(value) { + a.set({'foo': value}); + equals(a.is("foo"), true); + }); + }); + test("Model: set and unset", function() { attrs = { 'foo': 1, 'bar': 2, 'baz': 3}; a = new Backbone.Model(attrs); From 9f842bda94e31c06520696ead6add8441da65611 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Wed, 8 Dec 2010 11:28:13 -0500 Subject: [PATCH 010/167] Adding FAQ section for bootstrapping --- index.html | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/index.html b/index.html index df6000f33..dbb3d5a46 100644 --- a/index.html +++ b/index.html @@ -269,6 +269,7 @@

    + +

    + Loading Bootstrapped Models +
    + When your app first loads, it's common to have a set of initial models that + you know you're going to need, in order to render the page. Instead of + firing an extra AJAX request to fetch them, + a nicer pattern is to have their data already bootstrapped into the page. + You can then use refresh to populate your + collections with the initial data. At DocumentCloud, in the + ERB template for the + workspace, we do something along these lines: +

    + +
    +<script>
    +  Accounts.refresh(<%= @accounts.to_json %>);
    +  Projects.refresh(<%= @projects.to_json(:collaborators => true) %>);
    +</script>
     

    From 06a150627d98de9f7c75a80be41ae3a2928c2f21 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Wed, 8 Dec 2010 11:28:29 -0500 Subject: [PATCH 011/167] Adding FAQ section for bootstrapping --- index.html | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/index.html b/index.html index df6000f33..dbb3d5a46 100644 --- a/index.html +++ b/index.html @@ -269,6 +269,7 @@