Backbone.jsを使ってみて気になったこと

書籍の新着情報をチェックするWEBサービスmybookifyを作った時に、クライアントサイドのフレームワークとしてBackbone.jsを使用しました。

その時に気になったことがいくつかあったので、ここでまとめてみました。Backbone.jsの経験は少ないのでおかしな点があるかもです。何か気づいたことがありましたら教えてもらえると嬉しいです。

ここで使用したBackbone.jsのバージョンは1.1.2でした。

Model初期化時に引数を渡したい

Modelのメンバのデフォルト値は、defaultsを設定してあげれば良いです。

var Book = Backbone.Model.extend({
  defaults: {
    'title': 'MyBook'   // デフォルト値
  }
});

var book = new Book(); // => Object {title: "MyBook"}

ですが、インスタンス化するタイミングで値を設定したい時には次のようにオブジェクトを渡してあげます。

var Book = Backbone.Model.extend({
  defaults: {
    'title': 'MyBook'   // デフォルト値(この値は無視される)
  }
});

var book = new Book({
  'title': 'NewMyBook'
});
console.log(book.toJSON()); // => Object {title: "NewMyBook"}

ビューが2つあるとき、片方のビューで発生したイベントに対してもう片方のビューで処理したい時

これにはこちらで紹介されている方法が良さそうでした。

異なるビュー間のイベントを、どちらのビューからもアクセスできる別のオブジェクト(mediator)を経由する方法です。 次の例では、ViewAとViewBがある時に、ViewAでクリックされたイベントに対してViewBで処理を行っています。

// mediatorを定義
mediator = {};
_.extend(mediator, Backbone.Events);

// ViewAの定義
var ViewA = Backbone.View.extend({
  el: '#ViewA',

  events: {
    'click': 'OnViewAClick'            // 自分のビュー内のイベントを補足する
  },

  OnViewAClick: function() {
    mediator.trigger('OnViewAClick');  // mediatorにイベントを伝える
  }
});

// ViewBの定義
var ViewB = Backbone.View.extend({
  el: '#ViewB',

  initialize: function() {
    this.listenTo(mediator, 'OnViewAClick', this.OnViewBClick); // mediatorから伝わるイベントを拾う
  },

  OnViewBClick: function() {
    console.log('Hit!');
  }
});

ModelとREST APIとの連携

REST APIと対応付けるためには、Modelを次のように定義します。

var Book = Backbone.Model.extend({
  urlRoot: '/mybook-api',   // APIのurl

  idAttribute: '_id',       // ModelインスタンスのID
});

これによりGET、POST、PUT 、DELETEのリクエストを発行できるようになります。

発行先は/mybook-api/(_idの値)となり、それぞれ次のメソッドが対応しています。

var book = new Book();
// インスタンスがIDを持っていない時
console.log(book.isNew());  // => true

book.fetch();     // GET  .../mybook-api
book.save();      // POST .../mybook-api
book.destroy());  // リクエストは発行されない

// インスタンスがIDを持っている時
book.set('_id', 'myid');    // 仮にIDをmyidとしておく
console.log(book.isNew());  // => false

book.fetch();     // GET  .../mybook-api/myid
book.save();      // PUT .../mybook-api/myid
book.destroy();   // DELETE  .../mybook-api/myid

また、それぞれのメソッドではリクエスト成功時と失敗時の処理をコールバックで受け取ります。詳しくはドキュメントを確認して下さい。そしてこれらのメソッドは内部でsyncメソッドを呼んでいます。リクエストを細かくカスタマイズしたい場合はsyncメソッドを自分で再定義することになります。

続いて、モデルにparseメソッドを定義しておくと、サーバから返ってくるレスポンスを自分好みに処理できます。

var Book = Backbone.Model.extend({
    urlRoot: '/mybook-api',

    idAttribute: '_id',

    parse: function(response, options) {
      // レスポンス例: response == { status: 'success', book: { 'title': 'mybook' } }

      // レスポンスを処理して、モデルオブジェクトを返す
      return response.book;
    }
  });

複数のViewからアクセスしたいModelの持ち方(未解決)

BackboneではModelまたはCollectionはViewと1:1の関係になると思っています。

var MyModel = Backbone.Model.extend({});
var MyView = Backbone.View.extend({ model: MyModel });

var myModel = new MyModel();
var myView = new MyView({
  model: myModel
});

でも、Viewを越えて別のModelを参照したい時にはどうするのが良いのかわかりませんでした。

例えばAというModelとBというModelがあります。そしてViewAとViewBという、それぞれに対応するViewがあります。 このとき、ViewBでBを描画する際に、Aの値によって表示を変えるとします。

こんなときにどのような構造にすれば良いかわかりませんでした。 現状はAをグローバルなオブジェクトにして、どこからでもアクセスできるようにして対応しているのですが、あんまり綺麗じゃないような気がする。。。

取り敢えずこんなところです。まだちょっと思考錯誤しながらBackboneを使ってコードを綺麗にまとめられる方法を模索してます。もしかしたらAngularとか別のフレームワークに手を出したほうが良いのかなぁ?