Skip to content
This repository has been archived by the owner on Jul 12, 2020. It is now read-only.

Commit

Permalink
Merge remote-tracking branch 'upstream/gh-pages' into gh-pages
Browse files Browse the repository at this point in the history
  • Loading branch information
Marc Friedman committed Mar 31, 2013
2 parents 4391cad + fbed703 commit dd422c8
Show file tree
Hide file tree
Showing 5 changed files with 0 additions and 1,363 deletions.
Binary file modified backbone-fundamentals.epub
Binary file not shown.
369 changes: 0 additions & 369 deletions backbone-fundamentals.md
Original file line number Diff line number Diff line change
Expand Up @@ -11706,374 +11706,5 @@ From the source for `Backbone.history.start`:
`Backbone.History.stop` similarly uses your DOM manipulation library to unbind these event listeners.


## Upgrading to Backbone 0.9.10


*Developing Backbone.js Applications* is currently based on Backbone 0.9.10. If you are transitioning from 0.9.2 to 0.9.10 or above, the following is a guide of [changes](http://backbonejs.org/#changelog) grouped by classes, where applicable.

**Note:** We aim to update the entirety of this book to Backbone 1.0 once it has been tagged.

### Model

* Model validation is now only enforced by default in `Model#save` and is no longer enforced by default upon construction or in `Model#set`, unless the `{validate:true}` option is passed:

```javascript
var model = new Backbone.Model({name: "One"});
model.validate = function(attrs) {
if (!attrs.name) {
return "No thanks.";
}
};
model.set({name: "Two"});
console.log(model.get('name'));
// 'Two'
model.unset('name', {validate: true});
// false
```

* Passing `{silent:true}` on change will no longer delay individual `"change:attr"` events, instead they are silenced entirely.

```javascript
var model = new Backbone.Model();
model.set({x: true}, {silent: true});

console.log(!model.hasChanged(0));
// true
console.log(!model.hasChanged(''));
// true
```

* The `Model#change` method has been removed, as delayed attribute changes as no longer available.

* Calling `destroy` on a Model will now return `false` if the model `isNew`.

```javascript
var model = new Backbone.Model();
console.log(model.destroy());
// false
```

* After fetching a model or a collection, all defined parse functions will now be run. So fetching a collection and getting back new models could cause both the collection to parse the list, and then each model to be parsed in turn, if you have both functions defined.

* HTTP PATCH support allows us to send only changed attributes (i.e partial updates) to the server by passing `{patch: true}` i.e `model.save(attrs, {patch: true})`.

```javascript
// Save partial using PATCH
model.clear().set({id: 1, a: 1, b: 2, c: 3, d: 4});
model.save();
model.save({b: 2, d: 4}, {patch: true});
console.log(this.syncArgs.method);
// 'patch'
```

* When using `add` on a collection, passing `{merge: true}` will now cause duplicate models to have their attributes merged in to the existing models, instead of being ignored.

```javascript
var items = new Backbone.Collection;
items.add([{ id : 1, name: "Dog" , age: 3}, { id : 2, name: "cat" , age: 2}]);
items.add([{ id : 1, name: "Bear" }], {merge: true });
items.add([{ id : 2, name: "lion" }]); // merge: false

console.log(JSON.stringify(items.toJSON()));
// [{"id":1,"name":"Bear","age":3},{"id":2,"name":"cat","age":2}]

```

### Collection


* While listening to a [reset](http://backbonejs.org/#Collection-reset) event, the list of previous models is now available in `options.previousModels`, for convenience.

```javascript
var model = new Backbone.Model();
var collection = new Backbone.Collection([model])
.on('reset', function(collection, options) {
console.log(options.previousModels);
console.log([model]);
console.log(options.previousModels[0] === model); // true
});
collection.reset([]);
```

* `Collection#sort` now triggers a `sort` event, instead of a `reset` event.

* Removed `getByCid` from Collections. `collection.get` now supports lookup by both id and cid.

* Collections now also proxy Underscore method name aliases (`collect`, `inject`, `foldl`, `foldr`, `head`, `tail`, `take`, and so on...)

* Added `update` (which is also available as an option to fetch) for "smart" updating of sets of models.

The update method attempts to perform smart updating of a collection using a specified list of models. When a model in this list isn't present in the collection, it is added. If it is, its attributes will be merged. Models which are present in the collection but not in the list are removed.

```javascript
var theBeatles = new Collection(['john', 'paul', 'george', 'ringo']);

theBeatles.update(['john', 'paul', 'george', 'pete']);

// Fires a `remove` event for 'ringo', and an `add` event for 'pete'.
// Updates any of john, paul and georges's attributes that may have
// changed over the years.
```

* `collection.indexOf(model)` can be used to retrieve the index of a model as necessary.

```javascript
var col = new Backbone.Collection;

col.comparator = function(a, b) {
return a.get('name') < b.get('name') ? -1 : 1;
};

var tom = new Backbone.Model({name: 'Tom'});
var rob = new Backbone.Model({name: 'Rob'});
var tim = new Backbone.Model({name: 'Tim'});

col.add(tom);
col.add(rob);
col.add(tim);

console.log(col.indexOf(rob) === 0); // true
console.log(col.indexOf(tim) === 1); // true
console.log(col.indexOf(tom) === 2); // true

```

### View
* `View#make` has been removed. You'll need to use `$` directly to construct DOM elements now.
* When declaring a View, `options`, `el`, `tagName`, `id` and `className` may now be defined as functions, if you want their values to be determined at runtime.

### Events

* Backbone events now support jQuery-style event maps `obj.on({click: action})`. This is clearer than needing three separate calls to `.on` and should align better with the events hash used in Views:

```javascript
model.on({
‘change:name’ : this.nameChanged,
‘change:age’ : this.ageChanged,
‘change:height’ : this.heightChanges
});
```

* The Backbone object now extends Events so that you can use it as a global event bus, if you like.

* Backbone events now supports [once](http://backbonejs.org/#Events-once), similar to Node's [once](http://nodejs.org/api/events.html#events_emitter_once_event_listener), or jQuery's [one](http://api.jquery.com/one/). A call to `once()` ensures that the callback only fires once when a notification arrives.

```javascript
// Use once rather than having to explicitly unbind
var obj = { counterA: 0, counterB: 0 };
_.extend(obj, Backbone.Events);

var incrA = function(){ obj.counterA += 1; obj.trigger('event'); };
var incrB = function(){ obj.counterB += 1; };

obj.once('event', incrA);
obj.once('event', incrB);
obj.trigger('event');

console.log(obj.counterA === 1); // true
console.log(obj.counterB === 1); // true

```

`counterA` and `counterB` should only have been incremented once.

* Added [listenTo](http://backbonejs.org/#Events-listenTo) and [stopListening](http://backbonejs.org/#Events-stopListening) to Events. They can be used as inversion-of-control flavors of [on](http://backbonejs.org/#Events-on) and [off](http://backbonejs.org/#Events-off), for convenient unbinding of all events an object is currently listening to. `view.remove()` automatically calls `view.stopListening()`.

If you've had a chance to work on a few Backbone projects by this point, you may know that every `on` called on an object also requires an `off` to be called in order for the garbage collector to do its job.

This can sometimes be overlooked when Views are binding to Models. In 0.9.10, this can now be done the other way around - Views can bind to Model notifications and unbind from all of them with just one call. We achieve this using `view.listenTo(model, 'eventName', func)` and `view.stopListening()`.

The default `remove()` of Views will call `stopListening()` for you, just in case you don't remember to.

```javascript
var a = _.extend({}, Backbone.Events);
var b = _.extend({}, Backbone.Events);
a.listenTo(b, 'all', function(){ console.log(true); });
b.trigger('anything');
a.listenTo(b, 'all', function(){ console.log(false); });
a.stopListening();
b.trigger('anything');
```

A more complex example (from [Just JSON](http://justjson.blogspot.co.uk/)) might require our Views to respond to "no connection" and "connection resume" events to re-fetch data on demand in an application.

In 0.9.2, we have to do this to achieve what we need:

```javascript
// In BaseView definition
var BaseView = Backbone.View.extend({
destroy: function() {
// Allow child views to hook to this event to unsubscribe
// anything they may have subscribed to to other objects.
this.trigger('beforedestroy');
if (this.model) {
this.model.off(null, null, this);
}

if (this.collection) {
this.collection.off(null, null, this);
}

this.remove();
this.unbind();
}
});

// In MyView definition.
// We have a global EventBus that allows elements on the app to subscribe to global events.
// connection/disconnected, connection/resume is two of them.

var MyView = BaseView.extend({
initialize: function() {
this.on('beforedestroy', this.onBeforeDestroy, this);
this.model.on('reset', this.onModelLoaded, this);
EventBus.on('connection/disconnected', this.onDisconnect, this);
EventBus.on('connection/resume', this.onConnectionResumed, this);
},
onModelLoaded: function() {
// We only need this to be done once! (Kinda weird...)
this.model.off('load', this.onModelLoaded, this);
},
onDisconnect: function() {
// Figure out what state we are currently on, display View-specific messaging, etc.
},
onConnectionResumed: function() {
// Re-do previous network request that failed.
},
onBeforeDestroy: function() {
EventBus.off('connection/resume', this.onConnectionResumed, this);
EventBus.off('connection/disconnected', this.onDisconnect, this);
}
});
```

However, in 0.9.10, what we need to do is quite simple:

```javascript

// In BaseView definition
var BaseView = Backbone.View.extend({
destroy: function() {
this.trigger('beforedestroy');
this.remove();
}
});

// In MyView definition.

var MyView = BaseView.extend({
initialize: function() {
this.listenTo(EventBus, 'connection/disconnected', this.onDisconnect);
this.listenTo(EventBus, 'connection/resume', this.onConnectionResumed);
this.once(this.model, 'load', this.onModelLoaded);
},
onModelLoaded: function() {
// Don't need to unsubscribe anymore!
},
onDisconnect: function() {
// Figure out the state, display messaging, etc.
},
onConnectionResumed: function() {
// Re-do previous network request that failed.
}
// Most importantly, we no longer need onBeforeDestroy() anymore!
});

```


### Routers
* A "route" event is triggered on the router in addition to being fired on Backbone.history.

```javascript
Backbone.history.on('route', onRoute);

// Trigger 'route' event on router instance."
router.on('route', function(name, args) {
console.log(name === 'routeEvent');
});

location.replace('http://example.com#route-event/x');
Backbone.history.checkUrl();
```

* For semantic and cross browser reasons, routes will now ignore search parameters. Routes like `search?query=…&page=3` should become `search/…/3`.
* Bugfix for normalizing leading and trailing slashes in the Router definitions. Their presence (or absence) should not affect behavior.
* Router URLs now support optional parts via parentheses, without having to use a regex.

```javascript
var Router = Backbone.Router.extend({
routes: {
"optional(/:item)": "optionalItem",
"named/optional/(y:z)": "namedOptionalItem",
},
});
```

### Sync
* For mixed-mode APIs, `Backbone.sync` now accepts [emulateHTTP](http://backbonejs.org/#Sync-emulateHTTP) and [emulateJSON](http://backbonejs.org/#Sync-emulateJSON) as inline options.

```javascript
var Library = Backbone.Collection.extend({
url : function() { return '/library'; }
});

var attrs = {
title : "The Tempest",
author : "Bill Shakespeare",
length : 123
};

library = new Library;
library.create(attrs, {wait: false});

// update with just emulateHTTP
library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'}, {
emulateHTTP: true
});

console.log(this.ajaxSettings.url === '/library/2-the-tempest'); // true
console.log(this.ajaxSettings.type === 'POST'); // true
console.log(this.ajaxSettings.contentType === 'application/json'); // true

var data = JSON.parse(this.ajaxSettings.data);
console.log(data.id === '2-the-tempest');
console.log(data.author === 'Tim Shakespeare');
console.log(data.length === 123);

// or update with just emulateJSON

library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'}, {
emulateJSON: true
});

console.log(this.ajaxSettings.url === '/library/2-the-tempest'); // true
console.log(this.ajaxSettings.type === 'PUT'); // true
console.log(this.ajaxSettings.contentType ==='application/x-www-form-urlencoded'); // true

var data = JSON.parse(this.ajaxSettings.data.model);
console.log(data.id === '2-the-tempest');
console.log(data.author ==='Tim Shakespeare');
console.log(data.length === 123);

```

* Consolidated `"sync"` and `"error"` events within `Backbone.sync`. They are now triggered regardless of the existence of success or error callbacks.

* Added a `"request"` event to `Backbone.sync`, which triggers whenever a request begins to be made to the server. The natural complement to the `"sync"` event.


### Other
* Bug fix on change where attribute comparison uses `!==` instead of `_.isEqual`.
* Bug fix where an empty response from the server on save would not call the success function.
* To improve the performance of add, `options.index` will no longer be set in the `add` event callback.
* Removed the `Backbone.wrapError` helper method. Overriding sync should work better for those particular use cases.
* To set what library Backbone uses for DOM manipulation and Ajax calls, use `Backbone.$ =` ... instead of `setDomLibrary`.
* Added a `Backbone.ajax` hook for more convenient overriding of the default use of `$.ajax`. If AJAX is too passé, set it to your preferred method for server communication.
* Validation now occurs even during `"silent"` changes. This change means that the `isValid` method has been removed. Failed validations also trigger an error, even if an error callback is specified in the options.



---
Where relevant, copyright Addy Osmani, 2012-2013.
Loading

0 comments on commit dd422c8

Please sign in to comment.