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.scssapp/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
といっても前に自分で書いたものですが..


