【rails_vue_melt】Vue.jsにRailsから上手いことレコードデータを渡す
やりたいこと
Rails
のView側でVue.js
を連携させて、柔軟な処理をやりたい。元々これをやっていたプロダクトを事前に触っていたが、どういう実装だったのかそもそも知らなかったので後追いで学習してみた。
前勉強
そもそもvue.js
とかMVVM
とかよくわからない。最低でも単体で何ができるのか、概要を掴むために、自分が追った道筋。
Vue.jsではじめるMVVM入門 | DeNA DESIGN BLOG ここでvue.jsはそもそもどんなものなのか、書き方と大まかな流れを理解
大規模アプリケーションの構築 - vue.js いわゆるチュートリアル。このリンクのページが一番勉強になったが初めから追って書き方をなんとなくインプットした。
vue-cliで始めるVue.jsチュートリアル (1) - Qiita 改めて実装系の記事を読んだ。結構流れが掴めるようになってきた。
Vue.js + Vuexでデータが循環する全体像を図解してみた - Qiita
Vuex
というほぼ必須パーツのようなものの説明。これ含め、vue.js
で何ができるのかがだいたいわかったので目的達成。
RailsとVue.jsでデータを上手いこと受け渡す
ざっとみた感じ、方針は2つかと思います(多分)
vue.js
に別途Rails
側で新たに用意したAPIを叩かせて、レコードデータを取得するRails
のコントローラからビューに渡されたモデルのインスタンスをページにベタがきして、id
属性なんかを頼りにvue.js
に読ませる。
1は今までビューのコントローラがになっていた機能をわざわざ別に実装しなおすと言う、様々な面から良くない実装になりそうなので避けたい。となると、2を採用するのですが、取り決めをしっかりとしないと、ビューごとに受け渡しかたが違ってメンテしづらい読みづらいと地獄を見そうです。なので、上手いこと取り決めを作ってくれるものがあると、上手いこといきそう。
rails_vue_melt
vue_melt
の導入により、少なくとも、ビューごとにvue.js
へのデータを渡し方がバラバラになってしまう(複数人プロジェクトなら尚更)と言う問題を解決できそうです。
rails_vue_meltの導入
参考サイト。初めはこの方達の記事を参考に進めていきます。
【動画付き】Rails 5.1で作るVue.jsアプリケーション ~Herokuデプロイからシステムテストまで~ - Qiita
今回の環境(Mac OS)
$ bin/rails -v Rails 5.2.3 $ ruby -v ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin18] # Gemfile.lockより rails_vue_melt (0.2.0)
Gemfile
を編集し、webpacker
とrails_vue_melt
を導入します。
#Gemfile gem "rails_vue_melt" gem "webpacker"
webpacker
をインストールします。Yarn`がインストールされている必要があるので、まだの方は別途調べて導入してください。
$ yarn -v 1.16.0 $ bin/rails webpacker:install RAILS_ENV=development environment is not defined in config/webpacker.yml, falling back to production environment create config/webpacker.yml Copying webpack core config create config/webpack .... .... .... Webpacker successfully installed 🎉 🍰
vue
をインストールします。このとき生成されるhello_vue.js
,app.vue
の2ファイルはデモファイルで、vue_melt
の導入時に要らなくなるので削除して大丈夫だと思います。
$ rails webpacker:install:vue Copying vue loader to config/webpack/loaders create config/webpack/loaders/vue.js ... ...
次に下の通り進めることでapp/javascript/packs/vue_melt/
にvue_melt
に必要なファイルが生成されます。
$bundle install $bin/rails g vue_melt create app/javascript/packs/vue_melt create app/javascript/packs/vue_melt/application.js create app/javascript/packs/vue_melt/components/Hello.vue create app/javascript/packs/vue_melt/options/users.js create app/javascript/packs/vue_melt/store/actions.js create app/javascript/packs/vue_melt/store/getters.js create app/javascript/packs/vue_melt/store/index.js create app/javascript/packs/vue_melt/store/mutation-types.js create app/javascript/packs/vue_melt/store/mutations.js insert app/views/layouts/application.html.erb
https://qiita.com/midnightSuyama/items/efc5441a577f3d3abe74#css
ここで説明されているように、お好みでcssファイルに関しての設定をしてあげます。config/webpack/environment.js
に以下を記述
# config/webpack/environment.js environment.loaders.get('vue').options.extractCSS = false
config/webpack/loaders/vue.js
で設定を反映します。slim
を利用している場合、若干デフォルトに追記する必要あり。
#config/webpack/loaders/vue.js ... module.exports = { test: /\.vue(\.erb|\.slim)?$/, use: [ { loader: 'vue-loader', options: { extractCSS }, }, ], }
このまま進んでも良いですし、読み込みもとを変更することも可能です。これは、プロダクトが別途、管理画面等を作っているときのフォルダ分け程度の処置です。なんとなくapp/javascript/
に入れたくない人とか。下の変更を行ったら、上で生成したpacks
フォルダを移動先フォルダ下に移動させます。
#config/webpacker.yml source_path: app/[ここに移動先フォルダをかく]
View
から読み込めるようにします。vue_melt
はapplication.js
を起点に読み込みを始めているので、そこを指定するようです。またturbolinks
のキャッシュを切ります。app/views/layouts/application.html.erb
に以下を追記します。プロジェクトの設定によってはこの限りではないので(パーシャルに分けてたり)、レンダリングされる時に呼ばれる箇所に記述すれば大丈夫だと思います。
# app/views/layouts/application.html.erb <%= javascript_pack_tag 'vue_melt/application' %> <meta name="turbolinks-cache-control" content="no-cache">
私の環境ではturbolinks
を切っていたのでイベントが発火しませんでした。なので、app/[移動先(デフォルトはjavascript)]/packs/application.js
ロードタイミングをturbolinks:load
をDOMContentLoaded
に変更しました。これはそれぞれの環境に合わせてください。
// document.addEventListener('turbolinks:load', () => { document.addEventListener('DOMContentLoaded', () => { const templates = document.querySelectorAll('[data-vue]') for (const el of templates) { const vm = new Vue(Object.assign(options[el.dataset.vue], { el, store })) vms.push(vm) } })
動作テスト
ここまでで一度動くかテストをしたいので、デフォルトで用意されているのもを使用してもいいですし、自分で動くものを作って試してもokです。vue_melt
では、data-vue
属性で指定したjsファイルをpacks/options
下から探して対応づけしてくれます。例えばview画面に次のコードを書いたとして
<div data-vue="users"> <p>{{ message }}</p> </div>
呼ばれるjsファイルはpacks/options/users.js
という名前にして
export default { data: () => ({ message: 'Hello Users' }) }
のようなオプションオブジェクトを用意する事で、上手い事してくれます。
ではwebpack
を走らせてjsたちをコンパイルします。
$ bin/webpack
いろんなエラーが出ましたが、yarn add OO
で追加していきます。例として
Module not found: Error: Can't resolve 'lodash.clonedeep' in #=> $ yarn add lodash.clonedeep
コンパイルが成功したら、bin/rails s
でサーバを起動してしっかり動くか確認します。
無事私の手元では動きました。
Railsのレコードを扱う
レコードを扱うにはRails
のView側に渡したインスタンスをjson
にして渡す事で実現します。
data-vue-model
に渡す事で、いい具合に取り込んでくれます。例としてUser
モデルがあり、email
を保持していることとします。View
ページはこうなります。
# slimから適当に書き換えているので間違いがあるかもしれません、、 <section "data-vue"="users"> <%= content_tag :ul, 'data-vue-model': "{ \"users\": #{@users.to_json} }" do %> <li v-for="user in users"> id:{{user.id}},{{ user.email }} </li> <% end %> </section>
packs/options/user.js
を作成して、テンプレートを記述しておきます。これがないとエラー起こします。
# ここにmethodを記述すれば、上のviewページからv-on:clickなどに設定できる export default { data: ()=>{}, }
こんな感じで無事表示できました。
v-on:click
などに定義したmethod
を紐づけることで、ここから柔軟な操作などもできるようになる気がします。