冬休みがスーパー長かったのでクリスマスに買ってもらったVue入門本を斜め読みしながら、覚えられないコマンドメモサービス的なのを作りました。Railsのviewにちょろっと動きをつけた程度になります。
Commandware
ソース: https://github.com/yubachiri/commandware
この記事に書いてあるコードから動かなそうな雰囲気を感じたら動くものが置いてあるgithubを見てみてください。
あと結構適当なことを書いていると思います。適宜ご指摘いただけると幸いです。
やりたかったこと
- RailsでVue使いたかった
- ついでにVuexも使ってみたかった
- paneやりたかった(pivotal trackerみたいなの)(やりたかったことの実に10割以上を占める)
- 「git なかったことにする」をもう二度とググりたくなかった
書くこと
- 作ったものの解説
- Vue楽しい
Railsのviewにちょっと動きをつける程度のものでピンとくるものが見つけられなかったので書こうかと思いました
あとVue入門本よかった
準備
まず、rails newとか、DB設計と簡単なUI設計と一通りサービスが回るCRUDを実装しておきます。と言ってもコマンドゥェァは超質素。ER図はこんな感じです。いずれも記載同順で 1 - N です。wwwsqldesignerで作りました。
UIもこんな感じでちゃちゃっと手書き。迷いが見られますね。
そんで僕はnewする時にvue導入していなかったので、こちらの記事(https://qiita.com/cohki0305/items/582c0f5ed0750e60c951)に書いてあるような感じでvueを導入しました。
あと悲しみを背負ったのでTurbolinksは無効にしました。https://qiita.com/kazz187/items/12737363d62b9c91993c
中身概要
jsのファイル構造はこんな感じ。app直下です。使ってないものを消してないのには目をつむってください。
materializeのgridシステムの恩恵にあずかりつつ、まずはサイドバーとメイン部分をcol.s3
とcol.s9
で分けました。Vueを書くcommandware.js
も読み込んでおきます。
= javascript_pack_tag 'commandware'
# commandware
.row
/ サイドバー(flow一覧)
.col.s3
/ メイン
.col.s9
commandware.js
はこんな感じ。
Railsにリクエスト送るaxios
(https://github.com/axios/axios) はyarn add axios
で入れた。
import Vue from 'vue/dist/vue.esm'
import Vuex from 'vuex';
import store from '../store/index';
import axios from 'axios';
Vue.use(Vuex);
axios.defaults.headers.common = {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
};
document.addEventListener('DOMContentLoaded', () => {
const app = new Vue({
el: '#commandware',
store,
data: {略},
computed: {略},
methods: {略},
})
})
以下、ポイントを見ていきます。
サイドバーについて
ここですね。

コードはこちら
/ サイドバー(Flow一覧)
.col.s3
.collection.with-header
.collection-header
| Flows
.collection-item.new-flow-section
form @submit.prevent="postFlow"
input.new-flow-input type="text" placeholder="add by push Enter" v-model="newFlow"
.collection-item.clickable-item {v-for="flow in flows" :key="flow.id" @click="commandsDisplay(flow.id)"} = "{{flow.name}}"
commandware.jsのうち関係ある部分のみピックアップしたもの
document.addEventListener('DOMContentLoaded', () => {
const app = new Vue({
el: '#commandware',
store,
data: {
newFlow: '',
},
computed: {
flow: function () {
return store.state.flow
},
flows: function () {
return store.state.flows
},
},
methods: {
postFlow() {
axios.post(`/flows.json`,
flow: {
name: this.newFlow
}
})
.then(response => {
this.newFlow = ''
store.commit('addFlow', response.data)
})
.catch(error => {
alert('エラーが発生しました。')
})
},
},
})
})
/flows.json
にpostすると呼ばれるもの
class FlowsController < ApplicationController
before_action :set_flow, only: [:show, :edit, :update, :destroy]
# 略
def create
@flow = Flow.new(flow_params.merge(user: current_user))
respond_to do |format|
if @flow.save
format.html { redirect_to @flow, notice: 'Flow was successfully created.' }
format.json { render :show }
else
format.html { render :new }
format.json { render json: @flow.errors, status: 500}
end
end
end
# 略
private
def set_flow
@flow = Flow.find(params[:id])
end
def flow_params
params.require(:flow).permit(:name, :description)
end
end
というわけで長くなりましたが、この部分の解説をしてみたいと思います。
まずこれ、新しいflowの追加フォーム form @submit.prevent="postFlow"
ですが、@submit.prevent="postFlow"
では@submit(=v-on:submit)のイベントを止め、自身で定義したハンドラ(postFlow
)に投げます。submit自体はenter押下時に実行されますね。参考
呼ばれるpostFlow
ではaxiosでRailsにpostリクエストをパラメータを添えて投げています。
axios.post(`/flows.json`, {
flow: {
name: this.newFlow
}
})
this
はVueインスタンス自身、newFlow
はVueインスタンスに定義したdataです。
newFlowにはform内のinput.new-flow-input type="text" placeholder="add by push Enter" v-model="newFlow"
のv-model="newFlow"
でバインディングしているので、この欄に入力した値が入ります。
Railsからレスポンスが返ってくると、
axios.post(`/flows.json`, {
flow: {
name: this.newFlow
}
})
.then(response => {
this.newFlow = ''
store.commit('addFlow', response.data)
})
.catch(error => {
alert('エラーが発生しました。')
})
then
以降で受け取って処理していきます。response.data
にRailsがreturnしたものが入っています。今回の場合jsonです。
this.newFlow = ''
で入力欄を空にして、store.commit('addFlow', response.data)
ではVuexのmutationを呼び出し、Vuexが管理しているデータをいじります。この辺のフローは公式で学びましょう。https://vuex.vuejs.org/ja/
ただ、今回の規模でVuexを使うのは冗長かと思います。
import Vue from 'vue/dist/vue.esm'
import Vuex from 'vuex'
import getters from './getters'
import actions from './actions'
import mutations from './mutations'
Vue.use(Vuex)
const state = {
flows: gon.flows,
flow: {},
}
export default new Vuex.Store({
state,
getters,
actions,
mutations
})
export default {
addFlow(state, flow) {
state.flows.push(flow)
},
}
gon
はRailsからjsに値を渡す時に便利なやつです。https://github.com/gazay/gon
今回はindex.html.slim
を表示するアクション内でgon.flows = current_user.flows
という形でログイン中のユーザーが持っているflowを入れてあるので、初期ではこれを持っています。
axiosのpostが成功したら、追加したflowの情報がmutationを介してvuexのflows配列にpushされるわけですね。長い。
でサイドバーではこのflowsを一覧表示しています。flowsが更新されると同時に一覧表示も更新されます。
.collection-item.clickable-item {v-for="flow in flows" :key="flow.id" @click="commandsDisplay(flow.id)"} = "{{flow.name}}"
ここまでがサイドバーに表示されているものの一連の流れになります。
一旦ここまでにします。メイン部分は後日追記します。