LoginSignup
6
3

More than 3 years have passed since last update.

冬休みにRailsとVueでWebサービス作ったよ

Last updated at Posted at 2019-01-17

冬休みがスーパー長かったのでクリスマスに買ってもらった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で作りました。
スクリーンショット 2019-01-18 0.39.50.png
UIもこんな感じでちゃちゃっと手書き。迷いが見られますね。
iOS の画像 (6).jpg

そんで僕はnewする時にvue導入していなかったので、こちらの記事(https://qiita.com/cohki0305/items/582c0f5ed0750e60c951)に書いてあるような感じでvueを導入しました。
あと悲しみを背負ったのでTurbolinksは無効にしました。https://qiita.com/kazz187/items/12737363d62b9c91993c

中身概要

jsのファイル構造はこんな感じ。app直下です。使ってないものを消してないのには目をつむってください。
スクリーンショット 2019-01-18 0.57.05.png

materializeのgridシステムの恩恵にあずかりつつ、まずはサイドバーとメイン部分をcol.s3col.s9で分けました。Vueを書くcommandware.jsも読み込んでおきます。

index.html.slim(以下slimと記載)
= javascript_pack_tag 'commandware'

#commandware
  .row
    / サイドバー(flow一覧)
    .col.s3
    / メイン
    .col.s9

commandware.jsはこんな感じ。
Railsにリクエスト送るaxios(https://github.com/axios/axios) はyarn add axiosで入れた。

commandware.js
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: {},

  })
})

以下、ポイントを見ていきます。

サイドバーについて

ここですね。

スクリーンショット 2019-01-18 1.13.28.png

コードはこちら

slim
    / サイドバー(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のうち関係ある部分のみピックアップしたもの

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すると呼ばれるもの

flows_controller.rb
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リクエストをパラメータを添えて投げています。

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からレスポンスが返ってくると、

post箇所続き
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を使うのは冗長かと思います。

index.js(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
})

mutations.js
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が更新されると同時に一覧表示も更新されます。

slim
    .collection-item.clickable-item {v-for="flow in flows" :key="flow.id" @click="commandsDisplay(flow.id)"} = "{{flow.name}}"

ここまでがサイドバーに表示されているものの一連の流れになります。

一旦ここまでにします。メイン部分は後日追記します。

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3