Skip to content

Commit

Permalink
Issue jashkenas#8 -- a number of improvements to the documentation.
Browse files Browse the repository at this point in the history
  • Loading branch information
jashkenas committed Oct 14, 2010
1 parent eb9f54c commit 8310903
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 51 deletions.
35 changes: 20 additions & 15 deletions backbone.js
Expand Up @@ -322,17 +322,27 @@
// Add a model, or list of models to the set. Pass **silent** to avoid
// firing the `added` event for every new model.
add : function(models, options) {
if (!_.isArray(models)) return this._add(models, options);
for (var i=0; i<models.length; i++) this._add(models[i], options);
return models;
if (_.isArray(models)) {
for (var i = 0, l = models.length; i < l; i++) {
this._add(models[i], options);
}
} else {
this._add(models, options);
}
return this;
},

// Remove a model, or a list of models from the set. Pass silent to avoid
// firing the `removed` event for every model removed.
remove : function(models, options) {
if (!_.isArray(models)) return this._remove(models, options);
for (var i=0; i<models.length; i++) this._remove(models[i], options);
return models;
if (_.isArray(models)) {
for (var i = 0, l = models.length; i < l; i++) {
this._remove(models[i], options);
}
} else {
this._remove(models, options);
}
return this;
},

// Get a model from the set by id.
Expand Down Expand Up @@ -369,14 +379,8 @@
// you can refresh the entire set with a new list of models, without firing
// any `added` or `removed` events. Fires `refresh` when finished.
refresh : function(models, options) {
models || (models = []);
options || (options = {});
models = models || [];
var collection = this;
if (models[0] && !(models[0] instanceof Backbone.Model)) {
models = _.map(models, function(attrs, i) {
return new collection.model(attrs);
});
}
this._reset();
this.add(models, {silent: true});
if (!options.silent) this.trigger('refresh', this);
Expand Down Expand Up @@ -422,6 +426,9 @@
// hash indexes for `id` and `cid` lookups.
_add : function(model, options) {
options || (options = {});
if (!(model instanceof Backbone.Model)) {
model = new this.model(model);
}
var already = this.getByCid(model);
if (already) throw new Error(["Can't add the same model to a set twice", already.id]);
this._byId[model.id] = model;
Expand All @@ -432,7 +439,6 @@
model.bind('all', this._boundOnModelEvent);
this.length++;
if (!options.silent) this.trigger('add', model);
return model;
},

// Internal implementation of removing a single model from the set, updating
Expand All @@ -448,7 +454,6 @@
model.unbind('all', this._boundOnModelEvent);
this.length--;
if (!options.silent) this.trigger('remove', model);
return model;
},

// Internal method called every time a model in the set fires an event.
Expand Down
77 changes: 55 additions & 22 deletions index.html
Expand Up @@ -189,8 +189,8 @@
<li><a href="#Collection-sort">sort</a></li>
<li><a href="#Collection-pluck">pluck</a></li>
<li><a href="#Model-url">url</a></li>
<li><a href="#Collection-refresh">refresh</a></li>
<li><a href="#Collection-fetch">fetch</a></li>
<li><a href="#Collection-refresh">refresh</a></li>
<li><a href="#Collection-create">create</a></li>
</ul>
<a class="toc_title" href="#Sync">
Expand Down Expand Up @@ -975,23 +975,15 @@ <h2 id="Collection">Backbone.Collection</h2>
});
</pre>

<p id="Collection-refresh">
<b class="header">refresh</b><code>collection.refresh(models, [options])</code>
<br />
Adding and removing models one at a time is all well and good, but sometimes
you have so many models to change that you'd rather just update the collection
in bulk. Use <b>refresh</b> to replace a collection with a new list
of models (or attribute hashes), triggering a single <tt>"refresh"</tt> event
at the end. Pass <tt>{silent: true}</tt> to suppress the <tt>"refresh"</tt> event.
</p>

<p id="Collection-fetch">
<b class="header">fetch</b><code>collection.fetch([options])</code>
<br />
Fetch the default set of models for this collection from the server,
refreshing the collection when they arrive. The <b>options</b> hash takes
<tt>success</tt> and <tt>error</tt>
callbacks which will be passed <tt>(collection, response)</tt> as arguments.
When the model data returns from the server, the collection will
<a href="#Collection-refresh">refresh</a>.
Delegates to <a href="#Sync">Backbone.sync</a>
under the covers, for custom persistence strategies.
</p>
Expand All @@ -1007,10 +999,10 @@ <h2 id="Collection">Backbone.Collection</h2>
alert(method + ": " + model.url);
};

var accounts = new Backbone.Collection;
accounts.url = '/accounts';
var Accounts = new Backbone.Collection;
Accounts.url = '/accounts';

accounts.fetch();
Accounts.fetch();
</pre>

<p>
Expand All @@ -1020,6 +1012,27 @@ <h2 id="Collection">Backbone.Collection</h2>
for interfaces that are not needed immediately: for example, documents
with collections of notes that may be toggled open and closed.
</p>

<p id="Collection-refresh">
<b class="header">refresh</b><code>collection.refresh(models, [options])</code>
<br />
Adding and removing models one at a time is all well and good, but sometimes
you have so many models to change that you'd rather just update the collection
in bulk. Use <b>refresh</b> to replace a collection with a new list
of models (or attribute hashes), triggering a single <tt>"refresh"</tt> event
at the end. Pass <tt>{silent: true}</tt> to suppress the <tt>"refresh"</tt> event.
</p>

<p>
Here's an example using <b>refresh</b> to bootstrap a collection during initial page load,
in a Rails application.
</p>

<pre>
&lt;script&gt;
Accounts.refresh(&lt;%= @accounts.to_json %&gt;);
&lt;/script&gt;
</pre>

<p id="Collection-create">
<b class="header">create</b><code>collection.create(attributes, [options])</code>
Expand Down Expand Up @@ -1150,7 +1163,9 @@ <h2 id="View">Backbone.View</h2>
<tt>model</tt>, <tt>collection</tt>,
<tt>el</tt>, <tt>id</tt>, <tt>className</tt>, and <tt>tagName</tt>.
If the view defines an <b>initialize</b> function, it will be called when
the view is first created.
the view is first created. If you'd like to create a view that references
an element already in the DOM, pass in the element as an option:
<tt>new View({el: existingElement})</tt>
</p>

<pre>
Expand Down Expand Up @@ -1204,24 +1219,42 @@ <h2 id="View">Backbone.View</h2>
<br />
The default implementation of <b>render</b> is a no-op. Override this
function with your code that renders the view template from model data,
and updates <tt>this.el</tt> with the new HTML. You can use any flavor of
JavaScript templating or DOM-building you prefer. Because <b>Underscore.js</b>
is already on the page,
<a href="http://documentcloud.github.com/underscore/#template">_.template</a>
is already available. A good
and updates <tt>this.el</tt> with the new HTML. A good
convention is to <tt>return this</tt> at the end of <b>render</b> to
enable chained calls.
</p>

<pre>
var Bookmark = Backbone.View.extend({
render: function() {
$(this.el).html(this.template.render(this.model.toJSON()));
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
</pre>

<p>
Backbone is agnostic with respect to your preferred method of HTML templating.
Your <b>render</b> function could even munge together an HTML string, or use
<tt>document.createElement</tt> to generate a DOM tree. However, we suggest
choosing a nice JavaScript templating library.
<a href="http://github.com/janl/mustache.js">Mustache.js</a>,
<a href="http://github.com/creationix/haml-js">Haml-js</a>, and
<a href="http://github.com/sstephenson/eco">Eco</a> are all fine alternatives.
Because <a href="http://documentcloud.github.com/underscore/">Underscore.js</a> is already on the page,
<a href="http://documentcloud.github.com/underscore/#template">_.template</a>
is available, and is an excellent choice if you've already XSS-sanitized
your interpolated data.
</p>

<p>
Whatever templating strategy you end up with, it's nice if you <i>never</i>
have to put strings of HTML in your JavaScript. At DocumentCloud, we
use <a href="http://documentcloud.github.com/jammit/">Jammit</a> in order
to package up JavaScript templates stored in <tt>/app/views</tt> as part
of our main <tt>core.js</tt> asset package.
</p>

<p id="View-make">
<b class="header">make</b><code>view.make(tagName, [attributes], [content])</code>
<br />
Expand Down Expand Up @@ -1279,7 +1312,7 @@ <h2 id="View">Backbone.View</h2>
},

render: function() {
$(this.el).html(this.template.render(this.model.toJSON()));
$(this.el).html(this.template(this.model.toJSON()));
this.handleEvents();
return this;
},
Expand Down
33 changes: 19 additions & 14 deletions test/collection.js
Expand Up @@ -58,20 +58,6 @@ $(document).ready(function() {
equals(col.first(), d);
});

test("collections: refresh", function() {
var refreshed = 0;
var models = col.models;
col.bind('refresh', function() { refreshed += 1; });
col.refresh([]);
equals(refreshed, 1);
equals(col.length, 0);
equals(col.last(), null);
col.refresh(models);
equals(refreshed, 2);
equals(col.length, 4);
equals(col.last(), a);
});

test("collections: fetch", function() {
col.fetch();
equals(lastRequest[0], 'read');
Expand Down Expand Up @@ -111,4 +97,23 @@ $(document).ready(function() {
equals(col.min(function(model){ return model.id; }).id, 1);
});

test("collections: refresh", function() {
var refreshed = 0;
var models = col.models;
col.bind('refresh', function() { refreshed += 1; });
col.refresh([]);
equals(refreshed, 1);
equals(col.length, 0);
equals(col.last(), null);
col.refresh(models);
equals(refreshed, 2);
equals(col.length, 4);
equals(col.last(), a);
col.refresh(_.map(models, function(m){ return m.attributes; }));
equals(refreshed, 3);
equals(col.length, 4);
ok(col.last() !== a);
ok(_.isEqual(col.last().attributes, a.attributes));
});

});

0 comments on commit 8310903

Please sign in to comment.