JavaScript
Backbone.js

Backbone.js 使い方メモ

More than 3 years have passed since last update.

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


Backbone.js とは

CoffeeScript, Underscore.js などの作者である Jeremy Ashkenas が作っている JavaScript フレームワーク(jashkenas/backbone | Github)。

クライアントサイドで 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 ファイル


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


script.js

var MyModel = Backbone.Model.extend({

method: function() {
console.log('Hello Model!!');
}
});

var myModel = new MyModel();

myModel.method();



ブラウザコンソール出力

Hello Model!!



Collection の Hello World


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'));
});



ブラウザコンソール出力

[0] hoge

[1] fuga


View の Hello World


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


Model


基本操作


Model を作成する

var MyModel = Backbone.Model.extend();

var myModel = new MyModel();


  • Model を作成するには、 Backbone.Model.extend() を使う。


  • extend() の引数に、インスタンスメンバーの定義を渡す。

  • 戻り値はコンストラクタ関数になっていて、 new でインスタンスを生成できる。


インスタンスメソッドを定義する

var MyModel = Backbone.Model.extend({

instanceMethod: function() {
console.log('instance method');
}
});

var myModel = new MyModel();

myModel.instanceMethod();


コンソール出力

instance method



コンストラクタを定義する

var MyModel = Backbone.Model.extend({

constructor: function() {
console.log('constructor');
}
});

var myModel = new MyModel();


コンソール出力

constructor




  • constructor という名前のインスタンスメソッドを定義すると、インスタンス生成時に実行される。


  • initialize という名前でもいい。


  • constructorinitialize を両方定義すると、 constructor の方が実行される。

  • 後述する、既存の DOM を関連付ける場合は initialize で定義する。


    • DOM と紐付ける処理が constructor のデフォルト処理になっているようで、 constructor という名前で定義(オーバーライド)するとそのデフォルトの処理が実行されなくなる。




static メンバーを定義する

var MyModel = Backbone.Model.extend({}, {

staticField: 'static field',

staticMethod: function() {
console.log('static method');
}
});

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


コンソール出力

static field

static method



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


プロパティを設定・取得する

var MyModel = Backbone.Model.extend();

var myModel = new MyModel();

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


コンソール出力

field value




  • set(<名前>, <値>) で値を設定。


  • get(<名前>) で値を取得。


プロパティのデフォルト値を設定する

var MyModel = Backbone.Model.extend({

defaults: {
value: 'default value'
}
});

var myModel = new MyModel();

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


コンソール出力

default value



コンストラクタの引数でプロパティを設定する

var MyModel = Backbone.Model.extend();

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

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


コンソール出力

VALUE



プロパティを持つかどうかを確認する

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'));


コンソール出力

true

false
false



  • has(<名前>) で、指定した名前のプロパティが存在するかどうかを確認できる。

  • メソッドの存在は確認できない。


プロパティを削除する

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'));


コンソール出力

VALUE

undefined


プロパティを全て取得する

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'));


コンソール出力

VALUE

changed



  • attributes で全プロパティをオブジェクトで取得できる。

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


プロパティのコピーを取得する

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'));


コンソール出力

VALUE

VALUE



  • toJSON() でも同じようにプロパティを取得できる。

  • ここで取得したオブジェクトは、元のインスタンスが持つ attributes のコピーになる。


    • このオブジェクトのプロパティを変更しても、元のインスタンスに影響は与えない。

    • ただし、プロパティがさらにオブジェクトの場合、そのオブジェクトは元のインスタンスも参照しているので、変更すれば影響を与える。




バリデーションメソッドを定義する

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());


コンソール出力

true




  • validate という名前のインスタンスメソッドを定義する。


    • 引数に全プロパティをもつオブジェクトが渡される。

    • エラーが無い場合は、何も返さないようにする。

    • エラーが有る場合は、エラー情報を持つ任意の値を返す。




  • isValid() メソッドを実行すると validate() が実行され、エラーの有無が boolean で返される(エラーなし = true)。


エラー情報を参照する

var MyModel = Backbone.Model.extend({

validate: function(attrs) {
return 'error info';
}
});

var myModel = new MyModel();

myModel.isValid();

console.log(myModel.validationError);


コンソール出力

error info




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


インスタンスのコピーを作成する

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);


コンソール出力

Hoge

xxxx



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


値が書き変えられたプロパティを取得する

var MyModel = Backbone.Model.extend();

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

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

console.dir(myModel.changed);


コンソール出力

value   "change"




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


プロパティが変更されたかどうかを確認する

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'));


コンソール出力

false

true


Underscore.js のメソッドを使う

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


全てのプロパティの名前をリストで取得する

var MyModel = Backbone.Model.extend({

defaults: {
value: '',
name: ''
}
});

var myModel = new MyModel();

console.log(myModel.keys());


コンソール出力

["value", "name"]



全てのプロパティの値をリストで取得する

var MyModel = Backbone.Model.extend({

defaults: {
value: 'Value',
name: 'Name'
}
});

var myModel = new MyModel();

console.log(myModel.values());


コンソール出力

["Value", "Name"]



指定した名前のプロパティだけを抽出する

var MyModel = Backbone.Model.extend({

defaults: {
value: 'Value',
name: 'Name'
}
});

var myModel = new MyModel();

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


コンソール出力

Object { value="Value"}



指定した名前以外のプロパティだけを抽出する

var MyModel = Backbone.Model.extend({

defaults: {
value: 'Value',
name: 'Name'
}
});

var myModel = new MyModel();

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


コンソール出力

Object { name="Name"}



その他

pairsinvert も使える。


RESTful リクエスト

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


サーバーから Model の情報を取得する


レスポンスをそのまま Model のプロパティとして設定する

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 文字列を返すようにしておく。

{

"name": "Hoge"
}


コンソール出力

Hoge




  • urlRoot で、サーバーにリクエストするときの基準となるパスを設定する。

  • インスタンスを生成するときに、コンストラクタで id を渡す。


    • この id というプロパティは特別なプロパティで、 REST リクエストを実行する時のパスに自動的に埋め込まれる。

    • この場合 fetch() メソッドを実行すると、 /my-model/123 に GET リクエストが送られる。




  • fetch() メソッドは jqXHR オブジェクトを返す。


レスポンスを加工してから Model のプロパティに設定する

以下のように、サーバーからのレスポンスに Model 以外の情報が入っているようなケース。

{

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

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"));
});


コンソール出力

Hoge




  • parse() メソッドを定義することで、レスポンスを処理したうえで Model のプロパティを設定できる。


  • parse() メソッドの戻り値が、プロパティの情報を持つオブジェクトになるようにする。


(補足)id プロパティ

var MyModel = Backbone.Model.extend();

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

console.log(myModel.id);



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


POST または PUT でサーバーに Model の情報を送信する


POST リクエスト

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 リクエスト

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 にセットされる。


検証処理を挟む

var MyModel = Backbone.Model.extend({

urlRoot: '/my-model',

validate: function() {
console.log('validate');
}
});

var myModel = new MyModel();

myModel.save();


コンソール出力

validate




  • validate() メソッドが定義されている場合、 save() メソッドを実行した時に validate() メソッドが実行される。


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


DELETE リクエストを送信する

var MyModel = Backbone.Model.extend({

urlRoot: '/my-model'
});

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

myModel.destroy();



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


イベントの監視


イベントハンドラの登録とイベントの発火

var MyModel = Backbone.Model.extend();

var myModel = new MyModel();

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

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


コンソール出力

fierd myEvent. param = Parameter




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


  • trigger(<イベント名>) でイベントを発火できる。


イベントハンドラをアンバインドする


指定したイベントに紐づく全てのイベントハンドラをアンバインド

myModel.off('myEvent');


イベントとコールバック関数を指定してアンバインドする

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');


コンソール出力

callback2



コールバックを指定してアンバインドする

myModel.off(null, callback);


全てのイベントハンドラをアンバインドする

myModel.off();


1度だけコールバックされるイベントハンドラを登録する

var MyModel = Backbone.Model.extend();

var myModel = new MyModel();

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

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


コンソール出力

my event



他のモデルのイベントを監視する


イベントハンドラをバインドする

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');


コンソール出力

callback!!



イベントハンドラをアンバインドする

var observer = new MyModel();

var target = new MyModel();

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

observer.stopListening(target);



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


1度だけコールバックされる(ry

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


Collection, View でもイベントを監視する

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


Collection


基本操作


Collection を作成する

var MyCollection = Backbone.Collection.extend();

var myCollection = new MyCollection();



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


コンストラクタで初期値を設定する

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'));
});


コンソール出力

true

0 : Hoge
true
1 : Fuga


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


要素を追加する

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'));
});


コンソール出力

0 : Hoge

1 : Fuga
2 : Piyo


  • 単一のオブジェクト、配列、どちらでも追加できる。


  • push() でも同じように追加できる。


インデックスを指定して挿入する

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'));
});


コンソール出力

0 : Hoge

1 : Piyo
2 : Fuga



  • add() メソッドの第二引数にオプションを渡せる。


  • {at: <挿入先のインデックス値>} をオプションに指定することで、要素の挿入ができる。


先頭に要素を追加する

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'));
});


コンソール出力

0 : Piyo

1 : Hoge
2 : Fuga


インデックスを指定して要素を取得する

var MyCollection = Backbone.Collection.extend();

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

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


コンソール出力

Fuga




  • at() メソッドで、インデックス指定で要素を取得できる。


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


先頭の要素を削除する

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'));
});


コンソール出力

0 : Fuga



最後の要素を抜き取る

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'));
});


コンソール出力

Fuga

0 : Hoge


要素を指定して削除する

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'));
});


コンソール出力

0 : Fuga



全ての要素を削除する

var MyCollection = Backbone.Collection.extend();

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

console.log(myCollection.length);

myCollection.reset();

console.log(myCollection.length);


コンソール出力

3

0


全ての要素を入れ替える

var MyCollection = Backbone.Collection.extend();

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

console.log(myCollection.length);

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

console.log(myCollection.length);


コンソール出力

3

2


要素の数(サイズ)を取得する

var MyCollection = Backbone.Collection.extend();

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

console.log(myCollection.length);


コンソール出力

2



ソートする

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'));
});


コンソール出力

0 : aaa

1 : bbb
2 : ccc



  • sort() メソッドでソートできる。

  • このとき、 comparator を定義しておく必要がある。


  • comparator ではソートの基準となるプロパティの名前を指定する。


ソート方法を詳細に定義する

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'));
});


コンソール出力

0 : ccc

1 : bbb
2 : aaa



  • comparator に関数を指定すると、ソートの条件を詳細に定義できる。

  • 関数は以下のルールで戻り値を返すように実装する。


    • 第一引数 < 第二引数の場合は -1 を返す。

    • 第一引数 == 第二引数の場合は 0 を返す。

    • 第一引数 > 第二引数の場合は 1 を返す。




各要素から、指定したプロパティだけを抽出したリストを取得する

var MyCollection = Backbone.Collection.extend();

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

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

console.log(names);


コンソール出力

["Hoge", "Fuga", "Piyo"]



プロパティが指定した値の要素だけを抽出したリストを取得する

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'));
});


コンソール出力

0 : Hoge

1 : Piyo



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


プロパティが指定した値の要素のうち、先頭の要素だけを取得する

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'));


コンソール出力

Hoge



指定した範囲の要素を取得する

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'));
});


コンソール出力

0 : Fuga

1 : Piyo


要素の型を指定する

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();


コンソール出力

true

My name is Hoge.



  • model に Model のコンストラクタを指定する。

  • すると、要素に登録したオブジェクトは、指定した型のインスタンスに変換される。


Underscore.js のメソッドを使う

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

数が多い(28個)ので、 こちら を参照のこと。


REST リクエスト


コレクションの情報をサーバーから取得する

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'));
});
});

[

{"name": "Hoge"},
{"name": "Fuga"},
{"name": "Piyo"}
]

0 : Hoge

1 : Fuga
2 : Piyo



  • fetch() メソッドを使えば、サーバーから情報を GET リクエストで取得し、要素として設定することができる。

  • 基本的に Model の場合と同じ要領で使えるが、以下のような違いがある。



    • urlRoot ではなく、 url を設定する。

    • 最終的に、設定する値は配列になるようにする。




要素を追加し、サーバーに POST リクエストを送信する

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"}


コンソール出力

0 : Hoge

1 : Fuga
2 : Piyo



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


View

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

DOM 操作は普通に jQuery とかを使う。

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


View を作成する

var MyView = Backbone.View.extend();

var myView = new MyView();



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


コンストラクタを定義する

var MyView = Backbone.View.extend({

constructor: function() {
console.log('constructor');
}
});

var myView = new MyView();


コンソール出力

constructor



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


DOM オブジェクトを取得する

var MyView = Backbone.View.extend();

var myView = new MyView();

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


コンソール出力

[object HTMLDivElement]




  • el プロパティに DOM オブジェクトが設定されている。

  • デフォルトは、 div タグのオブジェクトになる。

  • この DOM オブジェクトは、あくまでメモリ上で生成されているもので、まだ HTML 上には表示されていない。


タグを指定する

var MyView = Backbone.View.extend({

tagName: 'span'
});

var myView = new MyView();

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


コンソール出力

[object HTMLSpanElement]




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


id 属性を設定する

var MyView = Backbone.View.extend({

id: 'my-id'
});

var myView = new MyView();

console.log(myView.el.id);


コンソール出力

my-id



class 属性を指定する

var MyView = Backbone.View.extend({

className: 'my-class'
});

var myView = new MyView();

console.log(myView.el.className);


コンソール出力

my-class



jQuery オブジェクトを取得する

$(function() {

var MyView = Backbone.View.extend();

var myView = new MyView();

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

backbone.JPG



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


既存の DOM を紐付ける

<!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>

$(function() {

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

var myView = new MyView();

myView.$el.css('color', 'red');
});

backbone.JPG



  • el プロパティに CSS セレクターを設定することで、既存の DOM オブジェクトを紐付けることができる。


  • el プロパティを設定した場合、 tagNameclassName などのプロパティの設定は無視される。


View の DOM オブジェクトを起点にして jQuery 関数を使用する

<!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>

$(function() {

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

var myView = new MyView();

console.log(myView.$('.foo').text());
});


コンソール出力

Hoge




  • $() メソッドを使うと、 View の DOM を起点にして jQuery 関数が使用できる。

  • これは、 myView.$el.find() と同じ操作になる。


render メソッド

$(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



  • render() メソッドは、デフォルトでは this を返すだけで何も処理をしない。


  • render() メソッドでは、 DOM に Model の情報を書き出したり、 DOM ツリー構造の構築を行う。


    • デフォルトの処理に合わせて、 this を返すようにしておくのが良い。



  • 別に、このメソッドが Backbone.js の何らかの処理から呼ばれるわけではなさそう。


    • つまり、前述のような初期化処理を render() という名前のメソッドで実装する必然性は無い。

    • しかし、 render() メソッドはデフォルトでは「何もしない」という振る舞いをするので、 Null オブジェクトパターン的な使い方ができるというアドバンテージがある。




イベントハンドリングの定義を一箇所にまとめる

<!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>

$(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 を作成する

var MyRouter = Backbone.Router.extend();

var myRouter = new MyRouter();



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


コンストラクタを定義する

var MyRouter = Backbone.Router.extend({

constructor: function() {
console.log('constructor');
}
});

var myRouter = new MyRouter();


コンソール出力

constructor



  • Model とかと同じ。


パスが変更されたときのイベントハンドラを定義する

var MyRouter = Backbone.Router.extend({

routes: {
'top': 'onTop'
},

onTop: function() {
console.log('onTop');
}
});

var myRouter = new MyRouter();

Backbone.history.start();

location.href = '#/top';


実行結果

onTop



  • キーに URL、値に関数名を設定したオブジェクトを、 routes プロパティに設定する。

  • キーで指定した URL に切り替わると、関数名で指定した関数が実行される。

  • Router を有効にするには、 Router インスタンスの初期化が完了した後に、 Backbone.history.start() を実行しなければならない。


パスパラメータを取得する

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';


コンソール出力

id = 1402



  • URL の定義に : 始まりの文字列を入れることで、その値を関数の引数で受け取ることができる。

  • 複数定義した場合は、定義した順序で関数の引数に渡される(名前は関係ない)。


パスを変更する

var MyRouter = Backbone.Router.extend({

routes: {
'test': 'test'
},

test: function() {
console.log('test');
}
});

var myRouter = new MyRouter();

Backbone.history.start();

myRouter.navigate('test');


コンソール出力




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


URL 切替時にイベントを発火させる

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});


コンソール出力

test




  • navigate() メソッドの第二引数に、設定を渡す。

  • 設定で、 trigger: true を指定すると、 URL 切替時にイベントが発火して、イベントハンドラが実行されるようになる。


パス変更時に履歴を残さない


デフォルトの動作

var MyRouter = Backbone.Router.extend();

var myRouter = new MyRouter();

Backbone.history.start();

myRouter.navigate('test');

backbone.JPG


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


履歴を残さない設定

var MyRouter = Backbone.Router.extend();

var myRouter = new MyRouter();

Backbone.history.start();

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

backbone.JPG


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


ハッシュバングを使わない

Backbone.history.start({pushState: true});


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


標準で用意されているイベント

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


Model


プロパティの変更を監視する

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');


コンソール出力

change property



特定のプロパティの変更だけを監視する

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);


コンソール出力

change age property




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


検証エラーのイベントを監視する

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();


コンソール出力

invalid. error=test error



Collection


要素の追加を監視する

var MyCollection = Backbone.Collection.extend();

var myCollection = new MyCollection();

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

myCollection.add('value');


コンソール出力

add model



要素の削除を監視する

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);


コンソール出力

remove model



Collection のリセットを監視する

var MyCollection = Backbone.Collection.extend();

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

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

myCollection.reset();


コンソール出力

reset collection



ソートを監視する

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();


コンソール出力

sort collection



RESTリクエスト関係のイベント


Model 削除のイベント

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();


コンソール出力

destroy model



サーバーにリクエストを送信したときのイベント


Model

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();


コンソール出力

request

request
request


Collection

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();


コンソール出力

request

request


サーバーリクエストが成功したときのイベント


Model

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();


コンソール出力

sync



Collection

var MyCollection = Backbone.Collection.extend({

url: '/my-collection'
});

var myCollection = new MyCollection();

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

myCollection.fetch();

sync


サーバーリクエストが失敗したときのイベント


Model

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();

error


Collection

var MyCollection = Backbone.Collection.extend({

url: '/unknown-url'
});

var myCollection = new MyCollection();

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

myCollection.fetch();

error


参考