既存のRailsプロジェクトにbackbone.jsを使ってみようと思い立って、backbone-on-railsを入れてみたのでメモ。
Gemfileに以下を追加する。
gem 'backbone-on-rails'
以下を実行する。
bundle install
rails generate backbone:install
そうすると、assets以下に以下のディレクトリができる。
- javascript/collections
- javascript/models
- javascript/routers
- javascript/views
- templates
templatesは、html置き場。
ecoというテンプレートエンジンを使って書ける。ecoはCoffeeScriptで記述するテンプレートだけれど、記述がほぼerbのように書けるので、erbで書いてきた人達にはストレスはないと思う。slimで書いている自分にはややストレス…。
試しにscaffoldでやってるベタなmodelに対してやってみる。
backboneのscaffoldを作成する。
rails generate backbone:scaffoild hoges
最初はアラートが出るようになっているので、まずコメントアウトしておく。
rails_project.js.coffeeは各々のプロジェクト名になっているので注意。
window.RailsProject =
Models: {}
Collections: {}
Views: {}
Routers: {}
initialize: ->
# alert 'Hello from Backbone!'
$(document).ready ->
RailsProject.initialize()
Hello World的なのは、他の記事を参考にしてください。よくあるから。
この記事では、次の順序でhogeリストを出すところまでやる。
- collectionを定義する。
- routerを定義する。
- collectionを処理するviewを定義する。
- 個別のmodelを処理するviewを定義する。
- routerを起動してデータを取ってくる。
1つ目はcollectionを定義。
class RailsProject.Collections.Hoges extends Backbone.Collection
model: RailsProject.Models.Hoge
url: 'hoges'
URLは、HogesクラスがHogeモデルのJSONを取りにいくパス。
fetchメソッドを呼ぶと、/hogesにAjaxでアクセスしてJSONを取ってくることを想定している。
2つ目に、routerを定義。
class RailsProject.Router.Hoges extends Backbone.Router
routes:
'': 'index'
initialize: ->
@collection = new RailsProject.Collections.Hoges()
@collection.fetch(reset: true)
index: ->
view = new RailsProject.Views.HogesIndex(collection: @collection)
$('#main').html(view.render().el)
routerからviewに複数のHogeを渡しています。
@collection.fetch(reset: true) が肝です。後で解説します。
3つ目に、collectionを処理するviewの定義。
class RailsProject.Views.HogesIndex extends Backbone.View
template: JST['hoges/index']
initialize: ->
@collection.on('reset', @render, this)
render: ->
$(@el).html(@template())
@collection.each(@appendHoges)
this
appendHoges: (hoge) ->
view = new RailsProject.Views.Hoge(model: hoge)
$('#hoges').append(view.render().el)
routerのところで言っていた、肝の部分ですが、initializeで@collectionのリセットが走ったらrenderメソッドを実行するように書いています。これは、Ajaxで@collectionを取ってくる前に、一度renderメソッドが実行されるため、@collectionが取得できたら再度renderを実行させるためです。
routerの@collection.fetchメソッドで、reset: trueに設定していたのは、フェッチし終わったらリセットしたと認識させるためです。私が参考にしていたサンプルだとこの部分が省略されていたので、すごくハマりました。バージョンが変わってfetchしたときのデフォルトの動作が変わったのかもしれません。
appendHogesメソッドで、4つ目で定義するクラスをインスタンス化して、ul#hogesに詰め込んでいます。そのul#hogeは、template: JST['hoges/index']で読み込んでいます。
では、templateを定義します。
<ul id="hoges"></ul>
このul#hogesの中にリストを表示するのがゴールとします。
4つ目に、個々のmodelを処理するviewを書きます。
class RailsProject.Views.Hoge extends Backbone.View
template: JST['hoges/age']
tagName: 'li'
render: ->
$(@el).html(@template(hoge: @model))
this
このviewで読み込むtemplateを定義します。
<%= @hoge.get('name') %>
backbone.jsで、モデルの要素を取りいく場合は、model.get('attribute_name')になります。model.attribute_nameというふうにはいかないので、注意が必要です。
5つ目に、このルーターを起動させます。
$ ->
# hogesコントローラーのときだけ実行するように書いたとして…
if $('#hoges_controller').is('*')
window.router = new RailsProject.Routers.Hoges()
Backbone.history.start()
これで、railsプロジェクトのhogesコントローラーにアクセスするとリストが表示される…はず…。