Backbone.js 使い方メモ

Backbone.js を勉強した時のメモ。

#Backbone.js とは
CoffeeScript, Underscore.js などの作者である Jeremy Ashkenas が作っている JavaScript フレームワーク（[jashkenas/backbone | Github](https://github.com/jashkenas/backbone/)）。

クライアントサイドで MVC を実装するための下地（骨組み）を提供する。

Angular.js がフルスタックで様々な機能が用意されているのに対して、 Backbone.js はあくまで Backbone（背骨）であり、骨組みを用意するのみ。

双方向のデータバインディングなどの機能はない。
肉付けをするのは、プログラマのお仕事。

機能が少ない分、覚えることは Angular.js に比べれば少ない、と思う。

#Hello World
##用意するファイル
###Backbone.js
http://backbonejs.org/#

本体。これがないと始まらない。

###Underscore.js（必須）
http://underscorejs.org/

Backbone.js は Underscore.js に依存している。
1.5.0 以上を導入する必要がある。

###jQuery（ほぼ必須）
http://jquery.com/

View および Router を使う場合は必要になるっぽい。

###json2.js（任意）
https://github.com/douglascrockford/JSON-js

古い IE （8 以下）とかで動かす場合に必要になるっぽい。
今回は使わない。

##HTML ファイル
```html:index.html
<!DOCTYPE html>
<html>
  <head>
    <script src="underscore-min.js"></script>
    <script src="jquery-2.1.1.min.js"></script>
    <script src="backbone-min.js"></script>
    <script src="script.js"></script>
  </head>
  <body>
  </body>
</html>
```

##Model の Hello World
```js:script.js
var MyModel = Backbone.Model.extend({
    method: function() {
        console.log('Hello Model!!');
    }
});

var myModel = new MyModel();

myModel.method();
```

```text:ブラウザコンソール出力
Hello Model!!
```

##Collection の Hello World
```js:script.js
var MyCollection = Backbone.Collection.extend({});

var myCollection = new MyCollection([
    {name: 'hoge'},
    {name: 'fuga'}
]);

myCollection.each(function(e, i) {
    console.log('[' + i + '] ' + e.get('name'));
});
```

```text:ブラウザコンソール出力
[0] hoge
[1] fuga
```

##View の Hello World
```js:script.js
$(function() {
    var MyView = Backbone.View.extend({
        render: function() {
            this.$el.text('Hello View!!');
            return this;
        }
    });

    var myView = new MyView();

    $('body').append(myView.render().el);
});
```

![backbone.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/f44a16b6-6f3d-9b52-cbc9-66801a3bcddf.jpeg)

#Model
##基本操作
###Model を作成する
```js:
var MyModel = Backbone.Model.extend();

var myModel = new MyModel();
```

- Model を作成するには、 `Backbone.Model.extend()` を使う。
- `extend()` の引数に、インスタンスメンバーの定義を渡す。
- 戻り値はコンストラクタ関数になっていて、 `new` でインスタンスを生成できる。

###インスタンスメソッドを定義する
```js:
var MyModel = Backbone.Model.extend({
    instanceMethod: function() {
        console.log('instance method');
    }
});

var myModel = new MyModel();

myModel.instanceMethod();
```

```text:コンソール出力
instance method
```

###コンストラクタを定義する
```js:
var MyModel = Backbone.Model.extend({
    constructor: function() {
        console.log('constructor');
    }
});

var myModel = new MyModel();
```

```text:コンソール出力
constructor
```

- `constructor` という名前のインスタンスメソッドを定義すると、インスタンス生成時に実行される。
- `initialize` という名前でもいい。
- `constructor` と `initialize` を両方定義すると、 `constructor` の方が実行される。
- 後述する、既存の DOM を関連付ける場合は `initialize` で定義する。
    - DOM と紐付ける処理が `constructor` のデフォルト処理になっているようで、 `constructor` という名前で定義（オーバーライド）するとそのデフォルトの処理が実行されなくなる。

###static メンバーを定義する
```js:
var MyModel = Backbone.Model.extend({}, {
    staticField: 'static field',
    
    staticMethod: function() {
        console.log('static method');
    }
});

console.log(MyModel.staticField);
MyModel.staticMethod();
```

```text:コンソール出力
static field
static method
```

- `extend()` メソッドの第二引数に渡したオブジェクトが static メンバーになる。

###プロパティを設定・取得する
```js:
var MyModel = Backbone.Model.extend();

var myModel = new MyModel();

myModel.set('value', 'field value');
console.log(myModel.get('value'));
```

```text:コンソール出力
field value
```

- `set(<名前>, <値>)` で値を設定。
- `get(<名前>)` で値を取得。

###プロパティのデフォルト値を設定する
```js:
var MyModel = Backbone.Model.extend({
    defaults: {
        value: 'default value'
    }
});

var myModel = new MyModel();

console.log(myModel.get('value'));
```

```text:コンソール出力
default value
```

###コンストラクタの引数でプロパティを設定する
```js:
var MyModel = Backbone.Model.extend();

var myModel = new MyModel({value: 'VALUE'});

console.log(myModel.get('value'));
```

```text:コンソール出力
VALUE
```

###プロパティを持つかどうかを確認する
```js:
var MyModel = Backbone.Model.extend({
    defaults: {
        value: 'VALUE'
    },
    method: function() {}
});

var myModel = new MyModel();

console.log(myModel.has('value'));
console.log(myModel.has('xxxxx'));
console.log(myModel.has('method'));
```

```text:コンソール出力
true
false
false
```

- `has(<名前>)` で、指定した名前のプロパティが存在するかどうかを確認できる。
- メソッドの存在は確認できない。

###プロパティを削除する
```js:
var MyModel = Backbone.Model.extend({
    defaults: {
        value: 'VALUE'
    }
});

var myModel = new MyModel();

console.log(myModel.get('value'));

myModel.unset('value');

console.log(myModel.get('value'));
```

```text:コンソール出力
VALUE
undefined
```

###プロパティを全て取得する
```js:
var MyModel = Backbone.Model.extend({
    defaults: {
        value: 'VALUE'
    }
});

var myModel = new MyModel();

var properties = myModel.attributes;

console.log(properties.value);

properties.value = 'changed';

console.log(myModel.get('value'));
```

```text:コンソール出力
VALUE
changed
```

- `attributes` で全プロパティをオブジェクトで取得できる。
- このオブジェクトの値を変更すると、元のインスタンスのプロパティも書き換わる。

###プロパティのコピーを取得する
```js:
var MyModel = Backbone.Model.extend({
    defaults: {
        value: 'VALUE'
    }
});

var myModel = new MyModel();

var properties = myModel.toJSON();

console.log(properties.value);

properties.value = 'changed';

console.log(myModel.get('value'));
```

```text:コンソール出力
VALUE
VALUE
```

- `toJSON()` でも同じようにプロパティを取得できる。
- ここで取得したオブジェクトは、元のインスタンスが持つ attributes のコピーになる。
    - このオブジェクトのプロパティを変更しても、元のインスタンスに影響は与えない。
    - ただし、プロパティがさらにオブジェクトの場合、そのオブジェクトは元のインスタンスも参照しているので、変更すれば影響を与える。

###バリデーションメソッドを定義する
```js:
var MyModel = Backbone.Model.extend({
    validate: function(attrs) {
        if (!attrs.value) {
            return 'value is empty.';
        }
    }
});

var myModel = new MyModel({value: 'xxxx'});

console.log(myModel.isValid());
```

```text:コンソール出力
true
```

- `validate` という名前のインスタンスメソッドを定義する。
    - 引数に全プロパティをもつオブジェクトが渡される。
    - エラーが無い場合は、何も返さないようにする。
    - エラーが有る場合は、エラー情報を持つ任意の値を返す。
- `isValid()` メソッドを実行すると `validate()` が実行され、エラーの有無が boolean で返される（エラーなし = `true`）。

####エラー情報を参照する
```js:
var MyModel = Backbone.Model.extend({
    validate: function(attrs) {
        return 'error info';
    }
});

var myModel = new MyModel();

myModel.isValid();

console.log(myModel.validationError);
```

```text:コンソール出力
error info
```

- `validate()` メソッドが返したエラー情報は、 `validationError` プロパティに保存されている。


###インスタンスのコピーを作成する
```js:
var MyModel = Backbone.Model.extend();

var original = new MyModel({
    value: {
        name: 'Hoge'
    }
});

var copy = original.clone();

console.log(copy.get('value').name);

copy.get('value').name = 'xxxx';

console.log(original.get('value').name);
```

```text:コンソール出力
Hoge
xxxx
```

- `clone()` メソッドでインスタンスのシャローコピーを作成できる。

###値が書き変えられたプロパティを取得する
```js:
var MyModel = Backbone.Model.extend();

var myModel = new MyModel({
    value: 'VALUE'
});

myModel.set('value', 'change');

console.dir(myModel.changed);
```

```text:コンソール出力
value   "change"
```

- `changed` に変更されたプロパティの情報が保存されている。

###プロパティが変更されたかどうかを確認する
```js:
var MyModel = Backbone.Model.extend();

var myModel = new MyModel({
    value: 'VALUE'
});

console.log(myModel.hasChanged('value'));

myModel.set('value', 'change');

console.log(myModel.hasChanged('value'));
```

```text:コンソール出力
false
true
```

##Underscore.js のメソッドを使う
Model インスタンスには、 Underscore.js が提供している一部の関数がメソッドとして定義されている。

###全てのプロパティの名前をリストで取得する
```js:
var MyModel = Backbone.Model.extend({
    defaults: {
        value: '',
        name: ''
    }
});

var myModel = new MyModel();

console.log(myModel.keys());
```

```text:コンソール出力
["value", "name"]
```

###全てのプロパティの値をリストで取得する
```js:
var MyModel = Backbone.Model.extend({
    defaults: {
        value: 'Value',
        name: 'Name'
    }
});

var myModel = new MyModel();

console.log(myModel.values());
```

```text:コンソール出力
["Value", "Name"]
```

###指定した名前のプロパティだけを抽出する
```js:
var MyModel = Backbone.Model.extend({
    defaults: {
        value: 'Value',
        name: 'Name'
    }
});

var myModel = new MyModel();

console.log(myModel.pick('value'));
```

```text:コンソール出力
Object { value="Value"}
```

###指定した名前以外のプロパティだけを抽出する
```js:
var MyModel = Backbone.Model.extend({
    defaults: {
        value: 'Value',
        name: 'Name'
    }
});

var myModel = new MyModel();

console.log(myModel.omit('value'));
```

```text:コンソール出力
Object { name="Name"}
```

###その他
[pairs](http://underscorejs.org/#pairs) と [invert](http://underscorejs.org/#invert) も使える。

##RESTful リクエスト
Model には、その Model を操作するための RESTful Web API を実行するメソッドが定義されている。

###サーバーから Model の情報を取得する
####レスポンスをそのまま Model のプロパティとして設定する
```js:
var MyModel = Backbone.Model.extend({
    urlRoot: '/my-model'
});

var myModel = new MyModel({id: 123});

myModel
    .fetch()
    .success(function() {
        console.log(myModel.get("name"));
    });
```

サーバーに `/my-model/123` の GET リクエストがあった場合、以下の JSON 文字列を返すようにしておく。

```json:
{
    "name": "Hoge"
}
```

```text:コンソール出力
Hoge
```

- `urlRoot` で、サーバーにリクエストするときの基準となるパスを設定する。
- インスタンスを生成するときに、コンストラクタで `id` を渡す。
    - この `id` というプロパティは特別なプロパティで、 REST リクエストを実行する時のパスに自動的に埋め込まれる。
    - この場合 `fetch()` メソッドを実行すると、 `/my-model/123` に GET リクエストが送られる。
- `fetch()` メソッドは [jqXHR](http://s3pw.com/qrefy/collectajax/) オブジェクトを返す。

####レスポンスを加工してから Model のプロパティに設定する
以下のように、サーバーからのレスポンスに Model 以外の情報が入っているようなケース。

```json:
{
    "error": false,
    "model": {"name": "Hoge"}
}
```

```js:
var MyModel = Backbone.Model.extend({
    urlRoot: '/my-model',
    
    parse: function(response) {
        return response.model;
    }
});

var myModel = new MyModel({id: 123});

myModel
    .fetch()
    .success(function() {
        console.log(myModel.get("name"));
    });
```

```text:コンソール出力
Hoge
```

- `parse()` メソッドを定義することで、レスポンスを処理したうえで Model のプロパティを設定できる。
- `parse()` メソッドの戻り値が、プロパティの情報を持つオブジェクトになるようにする。

####（補足）id プロパティ
```js:
var MyModel = Backbone.Model.extend();

var myModel = new MyModel({id: 123});

console.log(myModel.id);
```

- `id` プロパティは扱いが特別で、 `get()` メソッドを使わなくてもアクセスできる。

###POST または PUT でサーバーに Model の情報を送信する
####POST リクエスト
```js:
var MyModel = Backbone.Model.extend({
    urlRoot: '/my-model'
});

var myModel = new MyModel();

myModel.save({
    name: 'Hoge'
});
```

__サーバーに送信されたリクエスト__

|項目|値|
|:--|:--|
|URL|`/my-model`|
|Method|`POST`|
|Body|`{"name":"Hoge"}`|

####PUT リクエスト
```js:
var MyModel = Backbone.Model.extend({
    urlRoot: '/my-model'
});

var myModel = new MyModel({id: 123});

myModel.save({
    name: 'Hoge'
});
```

__サーバーに送信されたリクエスト__

|項目|値|
|:--|:--|
|URL|`/my-model/123`|
|Method|`POST`|
|Body|`{"id":123,"name":"Hoge"}`|

- `save()` メソッドを実行すると、 POST または PUT のリクエストが送信される。
    - `id` が設定されていない場合は POST、設定されていれば PUT のリクエストが送信される。
- 引数にオブジェクトを渡すと、 JSON 文字列に変換してリクエストの Body にセットされる。

###検証処理を挟む
```js:
var MyModel = Backbone.Model.extend({
    urlRoot: '/my-model',
    
    validate: function() {
        console.log('validate');
    }
});

var myModel = new MyModel();

myModel.save();
```

```text:コンソール出力
validate
```

- `validate()` メソッドが定義されている場合、 `save()` メソッドを実行した時に `validate()` メソッドが実行される。
- `validate()` メソッドが検証した結果、不正であると判定した場合、サーバーリクエストは実行されない。

###DELETE リクエストを送信する
```js:
var MyModel = Backbone.Model.extend({
    urlRoot: '/my-model'
});

var myModel = new MyModel({id: 123});

myModel.destroy();
```

- `/my-model/123` に DELETE メソッドのリクエストが送信される。


##イベントの監視
###イベントハンドラの登録とイベントの発火
```js:
var MyModel = Backbone.Model.extend();

var myModel = new MyModel();

myModel.on('myEvent', function(param) {
    console.log('fierd myEvent. param = ' + param);
});

myModel.trigger('myEvent', 'Parameter');
```

```text:コンソール出力
fierd myEvent. param = Parameter
```

- `on(<イベント名>, <コールバック>)` メソッドでイベントハンドラの登録ができる。
- `trigger(<イベント名>)` でイベントを発火できる。

###イベントハンドラをアンバインドする
####指定したイベントに紐づく全てのイベントハンドラをアンバインド
```js:
myModel.off('myEvent');
```

####イベントとコールバック関数を指定してアンバインドする
```js:
var MyModel = Backbone.Model.extend();

var callback1 = function() {console.log('callback1');};
var callback2 = function() {console.log('callback2');};

var myModel = new MyModel();

myModel.on('myEvent', callback1);
myModel.on('myEvent', callback2);

myModel.off('myEvent', callback1);

myModel.trigger('myEvent');
```

```text:コンソール出力
callback2
```

####コールバックを指定してアンバインドする
```js:
myModel.off(null, callback);
```

####全てのイベントハンドラをアンバインドする
```js:
myModel.off();
```

###１度だけコールバックされるイベントハンドラを登録する
```js:
var MyModel = Backbone.Model.extend();

var myModel = new MyModel();

myModel.once('myEvent', function() {
    console.log('my event');
});

myModel.trigger('myEvent');
myModel.trigger('myEvent');
```

```text:コンソール出力
my event
```

###他のモデルのイベントを監視する
####イベントハンドラをバインドする
```js:
var MyModel = Backbone.Model.extend({
    callback: function() {
        console.log('callback!!');
    }
});

var observer = new MyModel();
var target = new MyModel();

observer.listenTo(target, 'myEvent', observer.callback);

target.trigger('myEvent');
```

```text:コンソール出力
callback!!
```

####イベントハンドラをアンバインドする
```js:
var observer = new MyModel();
var target = new MyModel();

observer.listenTo(target, 'myEvent', observer.callback);

observer.stopListening(target);
```

- `off()` の時と同じ要領で、イベント指定・コールバック指定でアンバインドができる。

####１度だけコールバックされる（ｒｙ
```js:
observer.listenToOnce(target, 'myEvent', observer.callback);
```

###Collection, View でもイベントを監視する
後述する Collection や View でも、同様の方法でイベントハンドラの登録ができる。

#Collection
##基本操作
###Collection を作成する
```js:
var MyCollection = Backbone.Collection.extend();

var myCollection = new MyCollection();
```

- `Backbone.Collection.extend()` で Collection のコンストラクタ関数を作成できる。

###コンストラクタで初期値を設定する
```js:
var MyCollection = Backbone.Collection.extend();

var myCollection = new MyCollection([
    {name: 'Hoge'},
    {name: 'Fuga'}
]);

myCollection.each(function(e, i) {
    console.log(e instanceof Backbone.Model);
    console.log(i + ' : ' + e.get('name'));
});
```

```text:コンソール出力
true
0 : Hoge
true
1 : Fuga
```

- Collection に登録された各要素は、 Backbone.Model のインスタンスになる。

###要素を追加する
```js:
var MyCollection = Backbone.Collection.extend();

var myCollection = new MyCollection();

myCollection.add({name: 'Hoge'});

myCollection.add([
    {name: 'Fuga'},
    {name: 'Piyo'}
]);

myCollection.each(function(e, i) {
    console.log(i + ' : ' + e.get('name'));
});
```

```text:コンソール出力
0 : Hoge
1 : Fuga
2 : Piyo
```

- 単一のオブジェクト、配列、どちらでも追加できる。
- `push()` でも同じように追加できる。

####インデックスを指定して挿入する
```js:
var MyCollection = Backbone.Collection.extend();

var myCollection = new MyCollection([
    {name: 'Hoge'},
    {name: 'Fuga'}
]);

myCollection.add({name: 'Piyo'}, {at: 1});

myCollection.each(function(e, i) {
    console.log(i + ' : ' + e.get('name'));
});
```

```text:コンソール出力
0 : Hoge
1 : Piyo
2 : Fuga
```

- `add()` メソッドの第二引数にオプションを渡せる。
- `{at: <挿入先のインデックス値>}` をオプションに指定することで、要素の挿入ができる。

###先頭に要素を追加する
```js:
var MyCollection = Backbone.Collection.extend();

var myCollection = new MyCollection([
    {name: 'Hoge'},
    {name: 'Fuga'}
]);

myCollection.unshift({name: 'Piyo'});

myCollection.each(function(e, i) {
    console.log(i + ' : ' + e.get('name'));
});
```

```text:コンソール出力
0 : Piyo
1 : Hoge
2 : Fuga
```

###インデックスを指定して要素を取得する
```js:
var MyCollection = Backbone.Collection.extend();

var myCollection = new MyCollection([
    {name: 'Hoge'},
    {name: 'Fuga'}
]);

console.log(myCollection.at(1).get('name'));
```

```text:コンソール出力
Fuga
```

- `at()` メソッドで、インデックス指定で要素を取得できる。
- `get()` というメソッドもあるが、こちらはインデックスではなく Model の id で要素を指定する。

###先頭の要素を削除する
```js:
var MyCollection = Backbone.Collection.extend();

var myCollection = new MyCollection([
    {name: 'Hoge'},
    {name: 'Fuga'}
]);

myCollection.shift();

myCollection.each(function(e, i) {
    console.log(i + ' : ' + e.get('name'));
});
```

```text:コンソール出力
0 : Fuga
```

###最後の要素を抜き取る
```js:
var MyCollection = Backbone.Collection.extend();

var myCollection = new MyCollection([
    {name: 'Hoge'},
    {name: 'Fuga'}
]);

var topElement = myCollection.pop();

console.log(topElement.get('name'));

myCollection.each(function(e, i) {
    console.log(i + ' : ' + e.get('name'));
});
```

```text:コンソール出力
Fuga
0 : Hoge
```

###要素を指定して削除する
```js:
var MyCollection = Backbone.Collection.extend();

var myCollection = new MyCollection([
    {name: 'Hoge'},
    {name: 'Fuga'}
]);

var hoge = myCollection.at(0);

myCollection.remove(hoge);

myCollection.each(function(e, i) {
    console.log(i + ' : ' + e.get('name'));
});
```

```text:コンソール出力
0 : Fuga
```

###全ての要素を削除する
```js:
var MyCollection = Backbone.Collection.extend();

var myCollection = new MyCollection(['hoge', 'fuga', 'piyo']);

console.log(myCollection.length);

myCollection.reset();

console.log(myCollection.length);
```

```text:コンソール出力
3
0
```

####全ての要素を入れ替える
```js:
var MyCollection = Backbone.Collection.extend();

var myCollection = new MyCollection(['hoge', 'fuga', 'piyo']);

console.log(myCollection.length);

myCollection.reset(['foo', 'bar']);

console.log(myCollection.length);
```

```text:コンソール出力
3
2
```

###要素の数（サイズ）を取得する
```js:
var MyCollection = Backbone.Collection.extend();

var myCollection = new MyCollection([
    {name: 'Hoge'},
    {name: 'Fuga'}
]);

console.log(myCollection.length);
```

```text:コンソール出力
2
```

###ソートする
```js:
var MyCollection = Backbone.Collection.extend({
    comparator: 'name'
});

var myCollection = new MyCollection([
    {name: 'ccc'},
    {name: 'aaa'},
    {name: 'bbb'}
]);

myCollection.sort();

myCollection.each(function(e, i) {
    console.log(i + ' : ' + e.get('name'));
});
```

```text:コンソール出力
0 : aaa
1 : bbb
2 : ccc
```

- `sort()` メソッドでソートできる。
- このとき、 `comparator` を定義しておく必要がある。
- `comparator` ではソートの基準となるプロパティの名前を指定する。

####ソート方法を詳細に定義する
```js:
var MyCollection = Backbone.Collection.extend({
    comparator: function(left, right) {
        return left.get('name') < right.get('name');
    }
});

var myCollection = new MyCollection([
    {name: 'ccc'},
    {name: 'aaa'},
    {name: 'bbb'}
]);

myCollection.sort();

myCollection.each(function(e, i) {
    console.log(i + ' : ' + e.get('name'));
});
```

```text:コンソール出力
0 : ccc
1 : bbb
2 : aaa
```

- `comparator` に関数を指定すると、ソートの条件を詳細に定義できる。
- 関数は以下のルールで戻り値を返すように実装する。
    - 第一引数 < 第二引数の場合は `-1` を返す。
    - 第一引数 == 第二引数の場合は `0` を返す。
    - 第一引数 > 第二引数の場合は `1` を返す。

###各要素から、指定したプロパティだけを抽出したリストを取得する
```js:
var MyCollection = Backbone.Collection.extend();

var myCollection = new MyCollection([
    {name: 'Hoge'},
    {name: 'Fuga'},
    {name: 'Piyo'}
]);

var names = myCollection.pluck('name');

console.log(names);
```

```text:コンソール出力
["Hoge", "Fuga", "Piyo"]
```

###プロパティが指定した値の要素だけを抽出したリストを取得する
```js:
var MyCollection = Backbone.Collection.extend();

var myCollection = new MyCollection([
    {name: 'Hoge', age: 12},
    {name: 'Fuga', age: 14},
    {name: 'Piyo', age: 12}
]);

var array = myCollection.where({age: 12});

_.each(array, function(e, i) {
    console.log(i + ' : ' + e.get('name'));
});
```

```text:コンソール出力
0 : Hoge
1 : Piyo
```

- `filter()` でも実現できるが、条件がシンプルな場合は `where()` を使えば簡潔に書ける。

###プロパティが指定した値の要素のうち、先頭の要素だけを取得する
```js:
var MyCollection = Backbone.Collection.extend();

var myCollection = new MyCollection([
    {name: 'Hoge', age: 12},
    {name: 'Fuga', age: 14},
    {name: 'Piyo', age: 12}
]);

var element = myCollection.findWhere({age: 12});

console.log(element.get('name'));
```

```text:コンソール出力
Hoge
```

###指定した範囲の要素を取得する
```js:
var MyCollection = Backbone.Collection.extend();

var myCollection = new MyCollection([
    {name: 'Hoge', age: 12},
    {name: 'Fuga', age: 14},
    {name: 'Piyo', age: 12}
]);

var array = myCollection.slice(1, 3);

_.each(array, function(e, i) {
    console.log(i + ' : ' + e.get('name'));
});
```

```text:コンソール出力
0 : Fuga
1 : Piyo
```

###要素の型を指定する
```js:
var MyModel = Backbone.Model.extend({
    method: function() {
        console.log('My name is ' + this.get('name') + '.');
    }
});

var MyCollection = Backbone.Collection.extend({
    model: MyModel
});

var myCollection = new MyCollection([
    {name: 'Hoge', age: 12},
    {name: 'Fuga', age: 14},
    {name: 'Piyo', age: 12}
]);

var hoge = myCollection.at(0);

console.log(hoge instanceof MyModel);
hoge.method();
```

```text:コンソール出力
true
My name is Hoge.
```

- `model` に Model のコンストラクタを指定する。
- すると、要素に登録したオブジェクトは、指定した型のインスタンスに変換される。

##Underscore.js のメソッドを使う
Model 同様、 `forEach` や `map` など、リストで使える Underscore.js の関数がメソッドとして提供されている。

数が多い（28個）ので、 [こちら](http://backbonejs.org/#Collection-Underscore-Methods) を参照のこと。

##REST リクエスト
###コレクションの情報をサーバーから取得する
```js:
var MyCollection = Backbone.Collection.extend({
    url: '/my-collection'
});

var myCollection = new MyCollection();

myCollection
    .fetch()
    .success(function() {
        myCollection.each(function(e, i) {
            console.log(i + ' : ' + e.get('name'));
        });
    });
```

```json:
[
    {"name": "Hoge"},
    {"name": "Fuga"},
    {"name": "Piyo"}
]
```

```text:
0 : Hoge
1 : Fuga
2 : Piyo
```

- `fetch()` メソッドを使えば、サーバーから情報を GET リクエストで取得し、要素として設定することができる。
- 基本的に Model の場合と同じ要領で使えるが、以下のような違いがある。
    - `urlRoot` ではなく、 `url` を設定する。
    - 最終的に、設定する値は配列になるようにする。

###要素を追加し、サーバーに POST リクエストを送信する
```js:
var MyCollection = Backbone.Collection.extend({
    url: '/my-collection'
});

var myCollection = new MyCollection([
    {name: 'Hoge'},
    {name: 'Fuga'}
]);

myCollection
    .create({name: 'Piyo'}, {
        success: function() {
            myCollection.each(function(e, i) {
                console.log(i + ' : ' + e.get('name'));
            });
        }
    });
```

__サーバーに送信されたリクエスト__

|項目|値|
|:--|:--|
|URL|`/my-collection`|
|Method|`POST`|
|Body|`{"name":"Piyo"}`|

```text:コンソール出力
0 : Hoge
1 : Fuga
2 : Piyo
```

- `create()` は新しく追加された要素を return する。

#View
View といっても、これ自体がなにか DOM を操作する仕組みを提供してくれるわけではない。
DOM 操作は普通に jQuery とかを使う。

DOM 操作をするうえで便利になるプロパティとか（`el`, `$el` など）が `Backbone.View` によって提供される。

##View を作成する
```js:
var MyView = Backbone.View.extend();

var myView = new MyView();
```

- `Backbone.View.extend()` で View のコンストラクタ関数を取得できる。

##コンストラクタを定義する
```js:
var MyView = Backbone.View.extend({
    constructor: function() {
        console.log('constructor');
    }
});

var myView = new MyView();
```

```text:コンソール出力
constructor
```

- Model と同じで、 `constructor` または `initialize` という名前のインスタンスメソッドを定義すれば、インスタンス生成時に実行される。

##DOM オブジェクトを取得する
```js:
var MyView = Backbone.View.extend();

var myView = new MyView();

console.log(myView.el.toString());
```

```text:コンソール出力
[object HTMLDivElement]
```

- `el` プロパティに DOM オブジェクトが設定されている。
- デフォルトは、 `div` タグのオブジェクトになる。
- この DOM オブジェクトは、あくまでメモリ上で生成されているもので、まだ HTML 上には表示されていない。

###タグを指定する
```js:
var MyView = Backbone.View.extend({
    tagName: 'span'
});

var myView = new MyView();

console.log(myView.el.toString());
```

```text:コンソール出力
[object HTMLSpanElement]
```

- `tagName` プロパティを設定することで、タグを指定できる。

###id 属性を設定する
```js:
var MyView = Backbone.View.extend({
    id: 'my-id'
});

var myView = new MyView();

console.log(myView.el.id);
```

```text:コンソール出力
my-id
```

###class 属性を指定する
```js:
var MyView = Backbone.View.extend({
    className: 'my-class'
});

var myView = new MyView();

console.log(myView.el.className);
```

```text:コンソール出力
my-class
```

##jQuery オブジェクトを取得する
```js:
$(function() {
    var MyView = Backbone.View.extend();

    var myView = new MyView();

    myView.$el
          .text('jQuery object')
          .appendTo('body');
});
```

![backbone.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/89cb8431-fc1d-bc4c-c47f-50d57e794c1c.jpeg)

- `$el` プロパティに、 DOM オブジェクトをラップした jQuery オブジェクトが設定されている。

##既存の DOM を紐付ける
```html:
<!DOCTYPE html>
<html>
  <head>
    <script src="underscore-min.js"></script>
    <script src="jquery-2.1.1.min.js"></script>
    <script src="backbone-min.js"></script>
    <script src="script.js"></script>
  </head>
  <body>
    <div id="hoge">
      <ul>
        <li class="foo">#hoge ul .foo</li>
        <li class="bar">#hoge ul .bar</li>
      </ul>
      <ol>
        <li class="foo">#hoge ol .foo</li>
        <li class="bar">#hoge ol .bar</li>
      </ol>
    </div>
    
    <div id="fuga">
      <ul>
        <li class="foo">#fuga ul .foo</li>
        <li class="bar">#fuga ul .bar</li>
      </ul>
    </div>
  </body>
</html>
```

```js:
$(function() {
    var MyView = Backbone.View.extend({
        el: '#hoge ul .foo'
    });

    var myView = new MyView();
    
    myView.$el.css('color', 'red');
});
```

![backbone.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/8a571a98-e002-9f6d-3755-54947c3d88a9.jpeg)

- `el` プロパティに CSS セレクターを設定することで、既存の DOM オブジェクトを紐付けることができる。
- `el` プロパティを設定した場合、 `tagName` や `className` などのプロパティの設定は無視される。

##View の DOM オブジェクトを起点にして jQuery 関数を使用する
```html:
<!DOCTYPE html>
<html>
  <head>
    <script src="underscore-min.js"></script>
    <script src="jquery-2.1.1.min.js"></script>
    <script src="backbone-min.js"></script>
    <script src="script.js"></script>
  </head>
  <body>
    <div>
      <span class="foo">Hoge</span>
      <span class="bar">Fuga</span>
    </div>
    <span class="foo">Piyo</span>
  </body>
</html>
```

```js:
$(function() {
    var MyView = Backbone.View.extend({
        el: 'div'
    });

    var myView = new MyView();
    
    console.log(myView.$('.foo').text());
});
```

```text:コンソール出力
Hoge
```

- `$()` メソッドを使うと、 View の DOM を起点にして jQuery 関数が使用できる。
- これは、 `myView.$el.find()` と同じ操作になる。

##render メソッド
```js:
$(function() {
    var MyView = Backbone.View.extend({
        render: function() {
            this.$el.html('<b>Render</b>');
            return this;
        }
    });

    var myView = new MyView();
    
    $('body').append(myView.render().el);
});
```

![backbone.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/05c4039a-40de-d81e-f1c5-c666fbd33b0e.jpeg)

- `render()` メソッドは、デフォルトでは `this` を返すだけで何も処理をしない。
- `render()` メソッドでは、 DOM に Model の情報を書き出したり、 DOM ツリー構造の構築を行う。
    - デフォルトの処理に合わせて、 `this` を返すようにしておくのが良い。
- 別に、このメソッドが Backbone.js の何らかの処理から呼ばれるわけではなさそう。
    - つまり、前述のような初期化処理を `render()` という名前のメソッドで実装する必然性は無い。
    - しかし、 `render()` メソッドはデフォルトでは「何もしない」という振る舞いをするので、 Null オブジェクトパターン的な使い方ができるというアドバンテージがある。

##イベントハンドリングの定義を一箇所にまとめる
```html:
<!DOCTYPE html>
<html>
  <head>
    <script src="underscore-min.js"></script>
    <script src="jquery-2.1.1.min.js"></script>
    <script src="backbone-min.js"></script>
    <script src="script.js"></script>
  </head>
  <body>
    <div id="hoge">
      <button id="fuga">fuga</button>
      <button id="piyo">piyo</button>
    </div>
  </body>
</html>
```

```js:
$(function() {
    var MyView = Backbone.View.extend({
        el: '#hoge',
        
        events: {
            'click #fuga': 'onClickFuga',
            'click #piyo': 'onClickPiyo'
        },
        
        onClickFuga: function() {
            console.log('fuga');
        },
        
        onClickPiyo: function() {
            console.log('piyo');
        }
    });

    var myView = new MyView();
});
```

- ボタンをクリックすれば、それぞれコンソールに `fuga`, `piyo` と出力される。
- `events` プロパティで、監視するイベント・要素、そしてコールバックする関数のマッピングを定義できる。
    - 書式は、 `'<イベント> <セレクター>': '<コールバック関数名>'`。
    - セレクターは、 View オブジェクトの `el` 要素からの相対的な位置を指すように指定する。
    - セレクターを省略した場合、監視対象は View オブジェクトの `el` が指す要素になる。


#Router
SPA なページを作る時に必要になる URL の操作や、 URL 変更のイベントを制御するための仕組みを提供してくれる。

##Router を作成する
```js:
var MyRouter = Backbone.Router.extend();

var myRouter = new MyRouter();
```

- `Backbone.Router.extend()` でコンストラクタ関数を作成する。

##コンストラクタを定義する
```js:
var MyRouter = Backbone.Router.extend({
    constructor: function() {
        console.log('constructor');
    }
});

var myRouter = new MyRouter();
```

```text:コンソール出力
constructor
```

- Model とかと同じ。

##パスが変更されたときのイベントハンドラを定義する
```js:
var MyRouter = Backbone.Router.extend({
    routes: {
        'top': 'onTop'
    },
    
    onTop: function() {
        console.log('onTop');
    }
});

var myRouter = new MyRouter();

Backbone.history.start();

location.href = '#/top';
```

```text:実行結果
onTop
```

- キーに URL、値に関数名を設定したオブジェクトを、 `routes` プロパティに設定する。
- キーで指定した URL に切り替わると、関数名で指定した関数が実行される。
- Router を有効にするには、 Router インスタンスの初期化が完了した後に、 `Backbone.history.start()` を実行しなければならない。

###パスパラメータを取得する
```js:
var MyRouter = Backbone.Router.extend({
    routes: {
        'user/:id': 'userInfoPage'
    },
    
    userInfoPage: function(id) {
        console.log('id = ' + id);
    }
});

var myRouter = new MyRouter();

Backbone.history.start();

location.href = '#/user/1402';
```

```text:コンソール出力
id = 1402
```

- URL の定義に `:` 始まりの文字列を入れることで、その値を関数の引数で受け取ることができる。
- 複数定義した場合は、定義した順序で関数の引数に渡される（名前は関係ない）。

##パスを変更する
```js:
var MyRouter = Backbone.Router.extend({
    routes: {
        'test': 'test'
    },
    
    test: function() {
        console.log('test');
    }
});

var myRouter = new MyRouter();

Backbone.history.start();

myRouter.navigate('test');
```

```text:コンソール出力

```

- ブラウザの URL は変わるが、イベントハンドリングの関数は実行されない。

###URL 切替時にイベントを発火させる
```js:
var MyRouter = Backbone.Router.extend({
    routes: {
        'test': 'test'
    },
    
    test: function() {
        console.log('test');
    }
});

var myRouter = new MyRouter();

Backbone.history.start();

myRouter.navigate('test', {trigger: true});
```

```text:コンソール出力
test
```

- `navigate()` メソッドの第二引数に、設定を渡す。
- 設定で、 `trigger: true` を指定すると、 URL 切替時にイベントが発火して、イベントハンドラが実行されるようになる。

###パス変更時に履歴を残さない
####デフォルトの動作
```js:
var MyRouter = Backbone.Router.extend();

var myRouter = new MyRouter();

Backbone.history.start();

myRouter.navigate('test');
```

![backbone.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/e9ffecee-16de-491c-fb71-d4922f4b76cc.jpeg)

- デフォルトでは、パス変更前の履歴が残る。

####履歴を残さない設定
```js:
var MyRouter = Backbone.Router.extend();

var myRouter = new MyRouter();

Backbone.history.start();

myRouter.navigate('test', {replace: true});
```

![backbone.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/1d3408d4-c141-e828-7409-22c210f654f4.jpeg)

- 設定で、 `replace: true` を指定すると、パス変更前の履歴を残さないようにできる。

##ハッシュバングを使わない
```js:
Backbone.history.start({pushState: true});
```

- ハッシュバングを使わないようにする場合は、 `Backbone.history.start()` メソッドの引数に `{pushStatu: true}` を渡す。


#標準で用意されているイベント
イベントは自分で任意のものを作ることができるが、標準で用意されているイベントもある。

##Model
###プロパティの変更を監視する
```js:
var MyModel = Backbone.Model.extend();

var myModel = new MyModel({
    name: 'hoge'
});

myModel.on('change', function(model, options) {
    console.log('change property');
});

myModel.set('name', 'fuga');
```

```text:コンソール出力
change property
```

####特定のプロパティの変更だけを監視する
```js:
var MyModel = Backbone.Model.extend();

var myModel = new MyModel({
    name: 'hoge',
    age: 12
});

myModel.on('change:age', function(model, value, options) {
    console.log('change age property');
});

myModel.set('name', 'fuga');
myModel.set('age', 15);
```

```text:コンソール出力
change age property
```

- `change:<プロパティ名>` で、指定したプロパティの変更だけを監視できる。

###検証エラーのイベントを監視する
```js:
var MyModel = Backbone.Model.extend({
    validate: function() {
        return 'test error';
    }
});

var myModel = new MyModel();

myModel.on('invalid', function(model, error, options) {
    console.log('invalid. error=' + error);
});

myModel.isValid();
```

```text:コンソール出力
invalid. error=test error
```

##Collection
###要素の追加を監視する
```js:
var MyCollection = Backbone.Collection.extend();

var myCollection = new MyCollection();

myCollection.on('add', function(model, collection, options) {
    console.log('add model');
});

myCollection.add('value');
```

```text:コンソール出力
add model
```

###要素の削除を監視する
```js:
var MyCollection = Backbone.Collection.extend();

var myCollection = new MyCollection('hoge');

myCollection.on('remove', function(model, collection, options) {
    console.log('remove model');
});

var hoge = myCollection.at(0);
myCollection.remove(hoge);
```

```text:コンソール出力
remove model
```

###Collection のリセットを監視する
```js:
var MyCollection = Backbone.Collection.extend();

var myCollection = new MyCollection(['hoge', 'fuga', 'piyo']);

myCollection.on('reset', function(collection, options) {
    console.log('reset collection');
});

myCollection.reset();
```

```text:コンソール出力
reset collection
```

###ソートを監視する
```js:
var MyCollection = Backbone.Collection.extend({
    comparator: 'name'
});

var myCollection = new MyCollection([
    {name: 'hoge'},
    {name: 'fuga'},
    {name: 'piyo'}
]);

myCollection.on('sort', function(collection, options) {
    console.log('sort collection');
});

myCollection.sort();
```

```text:コンソール出力
sort collection
```

##RESTリクエスト関係のイベント
###Model 削除のイベント
```js:
var MyModel = Backbone.Model.extend({
    urlRoot: '/my-model'
});

var myModel = new MyModel({id: '123'});

myModel.on('destroy', function(model, collection, options) {
    console.log('destroy model');
});

myModel.destroy();
```

```text:コンソール出力
destroy model
```
###サーバーにリクエストを送信したときのイベント
###Model
```js:
var MyModel = Backbone.Model.extend({
    urlRoot: '/my-model'
});

var myModel = new MyModel({id: '123'});

myModel.on('request', function(model, xhr, options) {
    console.log('request');
});

myModel.fetch();
myModel.save();
myModel.destroy();
```

```text:コンソール出力
request
request
request
```

###Collection
```js:
var MyCollection = Backbone.Collection.extend({
    url: '/my-collection'
});

var myCollection = new MyCollection();

myCollection.on('request', function(collection, xhr, options) {
    console.log('request');
});

myCollection.fetch();
myCollection.create();
```

```text:コンソール出力
request
request
```

##サーバーリクエストが成功したときのイベント
###Model
```js:
var MyModel = Backbone.Model.extend({
    urlRoot: '/my-model'
});

var myModel = new MyModel({id: '123'});

myModel.on('sync', function(model, xhr, options) {
    console.log('sync');
});

myModel.fetch();
```

```text:コンソール出力
sync
```

###Collection
```js:
var MyCollection = Backbone.Collection.extend({
    url: '/my-collection'
});

var myCollection = new MyCollection();

myCollection.on('sync', function(collection, response, options) {
    console.log('sync');
});

myCollection.fetch();
```

```js:
sync
```

##サーバーリクエストが失敗したときのイベント
###Model
```js:
var MyModel = Backbone.Model.extend({
    urlRoot: '/unknown-url'
});

var myModel = new MyModel({id: '123'});

myModel.on('error', function(model, response, options) {
    console.log('error');
});

myModel.fetch();
```

```js:
error
```

###Collection
```js:
var MyCollection = Backbone.Collection.extend({
    url: '/unknown-url'
});

var myCollection = new MyCollection();

myCollection.on('error', function(collection, response, options) {
    console.log('error');
});

myCollection.fetch();
```

```js:
error
```

#参考
- [Backbone.js](http://backbonejs.org/#)
- [Backbone.js 日本語リファレンス](http://ando19721226.github.io/Backbone/)
- [Element - Web API インターフェイス | MDN](https://developer.mozilla.org/ja/docs/Web/API/element)
- [試して学ぶ Backbone.js入門 | Knowledge Note](http://appkitbox.com/knowledge/javascript/20130315-45)
- [Jeremy Ashkenas - Wikipedia, the free encyclopedia](http://en.wikipedia.org/wiki/Jeremy_Ashkenas)