LoginSignup
68
68

More than 5 years have passed since last update.

RailsアプリでBackbone.jsを使う

Last updated at Posted at 2012-06-29

Backbone.js Advent Calendar 5日目

Backbone.jsの本家ドキュメントにtodoリストをブラウザのLocalStorageを使って保存するチュートリアルがあります。

今回は、このtodoアプリのバックエンドとして、Ruby on Railsを使うように変更してみたいと思います。

Backbone.jsとサーバの通信

Backbone.jsはModelやCollectionの内容をサーバと同期するための手段を提供してくれています。標準で用意されているBackbone.syncはサーバがRESTfulと呼ばれるインタフェースを提供していることを前提に動作しますが、同期する方法を自作することも可能で、例えば上記のtodoリストのチュートリアルではLocalStorageにデータを保存するために、Storeという名前のオブジェクトを自作して用いています。そのため、Backbone.jsとサーバを同期する際は大きく分けて2通りの方法があることが分かります。

1.サーバにRESTfulなインタフェースを作り、標準のBackbone.syncを使う
2.独自のインタフェースを作り、独自の同期方法を実装する

Railsではscaffoldという機能を使うことで極めて簡単にRESTfulなAPIを作ることができるので、今回はこの機能を使ってみることにしましょう。

Railsの用意

$ rails new todos
$ cd todos
$ rails g scaffold todo text:string done:boolean
$ rake db:migrate

あとはunderscore.jsとbackbone.jsをapp/assets/javascriptsに追加し、underscore.jsがbackbone.jsより先に読み込まれるように、app/assets/javascripts/application.jsを以下のように変えます。

//= require jquery
//= require underscore
//= require backbone
//= require todos

Rubyを一行も書いていませんが、なんとこれで終わりです!あとはhtmlとjavascriptをがりがりと書いていくだけです

htmlとjavascriptを書く

元のチュートリアルのhtmlのbodyタグ内をapp/views/todos/index.html.erbにコピペします。このチュートリアルでは新しくTodoを作った時にDOMを作るのに、underscore.jsのテンプレート機能(とjQuery)を利用していますが、erbのロジックととunderscore.jsが標準で用意しているロジックが競合するため、そのままだとRailsがunderscore.jsのテンプレート用のロジックをerbのものだと勘違いしてエラーとなってしまいます。そこでtodos.jsに

_.templateSettings = {
  interpolate: /\{\{(.+?)\}\}/g,
  evaluate: /\{%(.+?)%\}/g,
  escape: /\{%-(.+?)%\}/g
};

と記述して標準のロジックを変更してしまうことにします。上記の記述では、Python用WebフレームワークであるDjangoのテンプレートエンジンに似た記法を採用しています。そしてこの新しいロジックに合うようにコピペしたhtml内の二つの<script type="text/template">タグの中身を以下のように変更します。

<script type="text/template" id="item-template">
  <div class="todo {{ done ? 'done' : '' }}">
    <div class="display">
      <input class="check" type="checkbox" {{ done ? 'checked="checked"' : '' }} />
      <div class="todo-text"></div>
      <span class="todo-destroy"></span>
    </div>
    <div class="edit">
      <input class="todo-input" type="text" value="" />
    </div>
  </div>
</script>
<script type="text/template" id="stats-template">
  {% if (total) { %}
    <span class="todo-count">
      <span class="number">{{ remaining }}</span>
      <span class="word">{{ remaining == 1 ? 'item' : 'items' }}</span> left.
    </span>
  {% } %}
  {% if (done) { %}
    <span class="todo-clear">
      <a href="#">
        Clear <span class="number-done">{{ done }}</span>
        completed <span class="word-done">{{ done == 1 ? 'item' : 'items' }}</span>
      </a>
    </span>
  {% } %}
</script>

最後にチュートリアルのjsをtodos.jsにコピペし、TodoListコレクションのlocalStorageプロパティを削除し、代わりにurlプロパティに"/todos"をセットします。次のようになるはずです

window.TodoList = Backbone.Collection.extend({
  model: Todo,

  // localStorage: new Store("todos"), 削除

  url: "/todos",

  done: function () {
    ...
  },
  ...
});

これで完成です。Railsを起動してアクセスしたら、動作していることが確認できるはずです。詳しい動作説明などは元のチュートリアルを参照してください。
このように、Railsのscaffold機能を用いることで、簡単にBackbone.jsのバックエンドを用意することができます。

おまけ

Backbone.jsはRESTfulなAPIを対象とすると書きましたが、ある程度のカスタマイズを行うことができます。

まず、APIのURLのカスタマイズを行うことができ、上記のように、ModelやCollectionのurlプロパティまたはメソッドやurlRootプロパティでカスタマイズすることができます。

そして、サーバから返されるjsonは初期状態では以下のような形式を期待されていますが

[{text: "hoge", done: false}, {text: "bar", done: true}]

例えば、他に検索ヒット数やページャのページ数などを同時に返して、以下のような形式になる場合は

{info: {results: 100, total_pages: 10, current_page:1},
todos: [{text: "hoge", done: false}, {text: "bar", done: true}]}

parseメソッドを用いてカスタマイズすることができます。上記の例の場合なら

var TodoList = Backbone.Collection.extend({
  ...
  parse: function (response) {
    this.info = response.info;  // ここでヒット件数の処理なども記述できる
    return response.todos  // 返される値がモデルにセットされる。Collectionだから配列を返している
  }
});
68
68
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
68
68