Rails に、MVVM の JavaScript ライブラリである Vue.js を導入し、データベースの中身を表示するまでやってみます。
Vue.js は レガシーブラウザ(例:IE8系)は非対応なので注意ください。
前提
- 手元の OS:
Mac OS X 10.9.5 (Mavericks)
- Ruby:
2.1.2
- Rails:
4.1.1
環境構築
① Railsのプロジェクト作成
$ bundle exec rails new hoge_app --skip-bundle -T
- テストは後々、Rspec を使いたいので
-T
を指定。- データベースはデフォルトの sqlite3 を今回は採用。
② Gemfileの編集
Vue.js だけを使うなら gem 'vuejs-rails'
のみでいいのですが、最低限のデザインの為の Twitter Bootstrap、テンプレート用の Haml、あとはデバックで必要そうな gem も導入しておきます。
#..
gem 'vuejs-rails'
gem 'bootstrap-sass'
gem 'haml-rails'
gem 'erb2haml', group: :development
gem 'better_errors', group: :development
gem 'binding_of_caller', group: :development
gem 'pry-rails', group: :development
#..
③ Bundler で gem をインストール
$ cd hoge_app
$ bundle install --path vendor/bundle
-
インストールが終わったら一旦、ここで動作確認。ブラウザで
http://127.0.0.1:3000
にアクセスし、お馴染みの画面が表示されればOK。$ cd hoge_app $ ./bin/rails s
④ Spring の有効化
とくに Vue.js とは関係ありませんが、やっておきます。
$ cd hoge_app
$ bundle exec spring binstub --all
⑤ Haml 関連の設定
既存の .erb
ファイルを .haml
ファイルへ変換。
$ cd hoge_app
$ ./bin/rake haml:replace_erbs
⑥ Vue.js 関連の設定
-
app/assets/javascripts/application.js
に追記。app/assets/javascripts/application.js〜 //= require vue 〜
⑦ Twitter Bootstrap 関連の設定
-
app/assets/stylesheets/application.css.scss
を新規に作成して編集。$ touch app/assets/stylesheets/application.css.scss
app/assets/stylesheets/application.css.scss@import "bootstrap-sprockets"; @import "bootstrap";
行の順番を入れ替えると動かないかもなので注意。
-
既存の
app/assets/stylesheets/application.css
は削除。$ rm app/assets/stylesheets/application.css
-
app/assets/javascripts/application.js
に追記。app/assets/javascripts/application.js〜 //= require bootstrap-sprockets 〜
作った環境の動作確認
① とりあえず適当に Scaffold
$ ./bin/rails g scaffold person name:string age:integer memo:text
$ ./bin/rake db:migrate
② Bootstrap の動作確認
-
共通ページレイアウトを修正。
app/views/layouts/application.html.haml!!! %html %head %title HogeApp = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true = javascript_include_tag 'application', 'data-turbolinks-track' => true = csrf_meta_tags %body %nav.navbar.navbar-default{role: "navigation"} .container-fluid .navbar-header %a.navbar-brand{href: "#"} home .container = yield
-
表示を確認。
$ ./bin/rails s
ブラウザで
http://127.0.0.1:3000/people
にアクセス。
③ Vue.js の動作確認
View
〜
#demo
%p
{{lastName}} {{firstName}}
%p
%input{"v-model" => "lastName"}/
%input{"v-model" => "firstName"}/
%br
%br
%p
{{message}}
%p
%button.btn.btn-primary{"v-on" => "click: execute"} 実行
Haml 記法です。
ViewModel
$ ->
demo = new Vue(
el: "#demo"
data:
firstName: "太郎"
lastName: "山田"
methods:
execute: ->
@message = "実行しました"
return
)
return
次のようにデータバインドが動いていればOK。
データベースの中身を Vue.js で描写してみる
Scaffold で作成した Person
モデルのデータを、一覧ページ(:index
)において Vue.js で描写してみます。
① Rails の View
%h1 Listing people
#index
:coffee
rails = window.rails = window.rails ? {}
rails.objPeople= #{raw @people.to_json}
%table.table.table-striped
%thead
%tr
%th 名前
%th 年齢
%th メモ
%tbody{'v-repeat' => 'items'}
%tr
%td
{{name}}
%td
{{age}}
%td
{{memo}}
%P
%a{:href => '/people/new'} New Person
CoffeeScript 部分では、rails = window.rails = window.rails ? {}
のようにして、この View 以外のファイルから参照できるよう、変数 rails
をグローバルにエクスポートしています。
rails.objPeople= #{raw @people.to_json}
により、Viewの出力時に Person
モデルのデータの中身が JavaScript のオブジェクトへ代入されます。
.. Vue.js はすっきり書けていいですね^^
② Vue.js の ViewModel
$(document).on('ready page:load', ->
if $("#index")[0]
new Vue(
el: "#index"
data:
items: rails.objPeople
)
return
)
new Vue()
により ViewModel を定義します。el
プロパティによりバインド対象とする div エリアを指定、data
プロパティでは、先程エクスポートした Person
モデルのデータを 変数 items
に紐づけます。
注意1
Rails の turbolinks 下で、この ViewModel を「初期読み込み時」「ページ遷移時」で動かすために次のようにしています。
$(document).on('ready page:load', ->
〜
)
上記の page:load
イベントは、純粋な jQuery のイベントではなく、turbolinks 用に拡張されたイベントです。
詳しくはこちら ⇒ https://github.com/rails/turbolinks
注意2
if $("#index")[0]
のようにして、「id='index'のdivエリアが存在する」場合に限って ViewModel を作成するようにしています。これは Rails が、どの Controller であるかに関わらず app/assets/javascripts/
ディレクトリ下のファイル全てを読み込んでしまうため、ほかの View 用の ViewModel を読み込んだ場合に div エリアが存在しない旨の JavaScript エラーが出てしまうからです。
③ 動作確認
Vue.js で描写できました^^
今回のソースは GitHub に置いておきます。手順通りやっても動かないぞ、という場合はこちらをご覧ください。
⇒ https://github.com/hkusu/Vue.js_Rails_seed
参考URL
といっても前に自分で書いたものですが..