セキュリティ系の勉強、その他開発メモとか雑談. GithubはUnity触っていた頃ものがメイン Twitterフォローもよろしくです

【rails_vue_melt】Vue.jsにRailsから上手いことレコードデータを渡す

//

やりたいこと

RailsのView側でVue.jsを連携させて、柔軟な処理をやりたい。元々これをやっていたプロダクトを事前に触っていたが、どういう実装だったのかそもそも知らなかったので後追いで学習してみた。

前勉強

そもそもvue.jsとかMVVMとかよくわからない。最低でも単体で何ができるのか、概要を掴むために、自分が追った道筋。

RailsとVue.jsでデータを上手いこと受け渡す

ざっとみた感じ、方針は2つかと思います(多分)

  1. vue.jsに別途Rails側で新たに用意したAPIを叩かせて、レコードデータを取得する
  2. Railsのコントローラからビューに渡されたモデルのインスタンスをページにベタがきして、id属性なんかを頼りにvue.jsに読ませる。


1は今までビューのコントローラがになっていた機能をわざわざ別に実装しなおすと言う、様々な面から良くない実装になりそうなので避けたい。となると、2を採用するのですが、取り決めをしっかりとしないと、ビューごとに受け渡しかたが違ってメンテしづらい読みづらいと地獄を見そうです。なので、上手いこと取り決めを作ってくれるものがあると、上手いこといきそう。



rails_vue_melt

qiita.com

github.com

vue_meltの導入により、少なくとも、ビューごとにvue.jsへのデータを渡し方がバラバラになってしまう(複数人プロジェクトなら尚更)と言う問題を解決できそうです。


rails_vue_meltの導入

参考サイト。初めはこの方達の記事を参考に進めていきます。

【動画付き】Rails 5.1で作るVue.jsアプリケーション ~Herokuデプロイからシステムテストまで~ - Qiita

Rails と Vue.js の設計覚書 - 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を編集し、webpackerrails_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_meltapplication.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:loadDOMContentLoadedに変更しました。これはそれぞれの環境に合わせてください。

// 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でサーバを起動してしっかり動くか確認します。
f:id:thinline196:20191125130116p:plain
無事私の手元では動きました。



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: ()=>{},
}


こんな感じで無事表示できました。 f:id:thinline196:20191125164037p:plain
v-on:clickなどに定義したmethodを紐づけることで、ここから柔軟な操作などもできるようになる気がします。