Rails + Vue.jsでwebアプリケーション開発

Railsのwebpackを使用して、SPAの開発ができるということで、実際に開発しているが、あまり、Rails + Vue.jsの開発メモがなかったので、はまったところなどメモしたいと思う。
開発環境の構築、Vue.jsの使い方などは、今回は割愛させていただきます。

### 2019/01/27 後述
RailsとVue.jsを共存させるというのは難しいなと一通り開発してみて感じた。本来、コードをスマートにするために使われるフレームワークの良さが失われてしまう。2018年の1年間フロントまわりの技術をさらってみて、本記事のようにサーバーサイドとフロントを共存させてしまうよりも、サーバーサイドはAPIとしての役割のみ持たせて(RailsにはAPIモードがある)、あとはフロントに任せるという、双方のコードを分離させた方が開発がスムーズになると今は考えている。そのため本記事の方法はあまり推奨しない。


### 環境情報
Ruby 2.4.0
Rails 5.1.0
rails/webpacke 3.1.0
Vue.js 2.5.13
vue-router3.0.1

### Vueファイルを使用する
vue.jsを導入した時にできるファイルhello_vue.jsがvue.jsを動かす基盤となる。

#### トップページを作ってみる

```javascript:app/javascript/packs/hello_vue.js
import Vue from 'vue/dist/vue.esm'
import router from './routes.js';

document.addEventListener('DOMContentLoaded', () => {
  const app = new Vue({
    el: '#app',
    router
  })
});

```
importで、yarnで導入したライブラリを読み込む。この場合だと、vue/dist/vue.esmを使用するよ〜、./routes.js(こいつは、後で触れるが、ルーティングの設定をしているファイル)を使用する宣言をしている。それぞれ、Vueやrouterという名前をつけて、Vueのインスタンス化をするときに、読んであげる。それが、new Vue({})の中である。`el: '#app'`というのは、Vueのルールで、このappで囲まれたところでしか、Vueは使えない。


#### vue-routerを使用する
vue.jsのルーティングはvue-routerを使用してあげる。先ほどの、hello_vue.jsに記述した。

```javascript:app/javascript/packs/routes.js
import Vue from 'vue/dist/vue.esm';
import VueRouter from 'vue-router';
import Index from './components/app.vue';

Vue.use(VueRouter);

const router = new VueRouter({
  mode: 'history',
    routes: [
    { path: '/', component: Index, name: 'root_path' },
  ]
});

export default router;
```

先ほどのhello_vue.jsと同様。どのファイルを使用するかを記述する。
`{ path: 'アクセスされるURL', '使用するコンポーネント', 'pathの名前'}`
こんな感じのルールで書く。これで、トップページにアクセスすると、app.vueで記述した画面に遷移するように設定ができた。

ここまで、作ったものをレイアウトに乗せてあげればOK。
あとは、このrouterを使用するために、Railsのapplication.html.erbにrouter-viewタグを追加する。

```erb:application.html.erb
<head>
 <%= javascript_pack_tag 'hello_vue' %>
</head>
<body>
  <div id="app">
    <router-view></router-view>
  </div>
</body>
```

こうすることによって、先ほど、hello_vue.jsで書いたid = appの下はvue.jsが動くので、appのdivでrouter-viewを挟んであげれば、指定されたpathに対して、componentsを描画するというようにvue.jsが動くようになる。

### 実際に開発する
てな、感じでトップページは作ることは簡単にできるのだが、実際に開発してみるとハマるポイントが多い。
実際に作業の流れを例に示す。

#### ①vue.jsでフォームを作る
まずは、先ほどと同じ要領で、ルーティングの設定をする。

```javascript:app/javascript/packs/routes.js
import Vue from 'vue/dist/vue.esm';
import VueRouter from 'vue-router';
import Index from './components/app.vue';
import Question from './components/question.vue';

Vue.use(VueRouter);

const router = new VueRouter({
  mode: 'history',
    routes: [
    { path: '/', component: Index, name: 'root_path' },
    { path: '/question/new', component: Question, name: 'question_new_path' },
  ]
});

export default router;
```

question/newというパスにきたら、Questionコンポーネントを使用するようにルーティング設定。Questionコンポーネントは、question.vueというファイルをcomponentsの下に新規追加する。

```vue:app/javascript/packs/components/question.vue
<template>
    <div>
        <div class="container">
            <form>
                <div class="form-group">
                    <label for="form_title">質問名</label>
                    <input type="text" name="question[content]" class="form-control" id="content">
					<button type="button" class="btn btn-primary" v-on:click="postQuestion">作成する</button>
                </div>
	　　　　　　　　　　　　　　　　</form>
		</div>
    </div>
</template>
<script>
    export default {
        data: function () {
            return {
                question: {
                    content: null,
                },
            }
        },
        methods: {
        }
    }
</script>
				
```

ただ、これだけだと、/question/newにアクセスしてもRailsのルーティングエラーになってしまう。
**そのため、Railsの開発同様、Question_cotroller.rbとviews/questions/new.html.erb、そして、rails側のルーティングも記述しないといけない。**

#### ②保存処理を実装する
ここまでで、DBなどを活用しない画面は作ることができるが、やはりシステムを作るなら、DBに値を保存する機能が必須である。
ここからは、Vue側から、Railsに値を渡してデータを保存する機能を作って行く。

データを保存する際には、ajax処理を使うのだが、せっかくVue.jsを使っているので、axiosを使用する。
`yarn add axios`でaxiosをインストールしたら、

```vue:app/javascript/packs/components/question.vue
<script>
    import axios from 'axios'

    export default {
        data: function () {
            return {
                question: {
                    content: null,
                },
		question_id: null
            }
        },
        methods: {
         postQuestion: function(){
           axios.post('/api/quetion', {
             content: this.question.content,
          }).then((res)
             this.question_id = res.data._id
          });
         }
       }
    }
</script>
				
```

postの第一引数に、保存処理をするコントローラーのパス、第二引数に値を渡せば簡単にajax処理ができる。

```ruby:/api/question_controller.rb
	def create
		begin
			@question = Question.create!(question_params)
			respond_to do |format|
				format.html { redirect_to @question, notice: 'Question was successfully created.' }
				format.json { render :new, status: :created, location: @question }
			end
		rescue => e
			logger.error(e.message)
			format.html { render :new }
			format.json { render json: @question.errors, status: :unprocessable_entity }
		end

	end

	def question_params
		params.require(:question).permit(
			:content
		)
	end
```
基本的に、サーバーサイドに値が渡ってきたら、Railsのやり方でやればOK

#### ③サーバーサイドからの値を取得する
Railsの場合、コントローラーから変数をerbファイルに渡すことができたが、vue.jsの場合はそれができない。
そのため、基本的に値はコントローラーでjsonデータをjson.jbuilderを使ってview側に渡す形になる。

```jbuilder:/views/api/question.json.jbuilder
json.question_id @question.id
```
基本的に、これらの要領でいけば、VueとRailsを使ってデータのやり取りができるはず。

### 参照
[Vue.jsとRailsでTODOアプリのチュートリアルみたいなものを作ってみた](https://qiita.com/naoki85/items/51a8b0f2cbf949d08b11)
[Vuejs – RE:ENGINES](https://re-engines.com/tag/vuejs/)
[Rails 5.1 + Vue.js で開発を行う - part2 Ajax で GET しよう！](https://qiita.com/cohki0305/items/a678b0b17c5b496c1de9)