Rails+Vue.js+Webpackerによる「Create, Read, Update, Destroy」処理のチュートリアルを記述する。
なお,前提知識として,Railsチュートリアル終了程度を想定する。
<概要>
■ 本記事の内容
- 前回作成したViewをVueのコンポーネントへ移行する。
- SPAに必要なVue Routerを設定する。
- ルーティング用のヘッダーを作成する。
- 今回のコードは,GitHubのコミット履歴で確認可能である。
- [前回分の修正]から[不要データの削除]まで
- 本記事の参考URL
■ 記事共通
-
目次
-
実装機能
- お気に入りの本に係る登録,参照,編集,削除処理
- 非同期通信(Ajax)による[Rails+Vue.js]のCRUD処理
- SinglePageApplication(SPA)
- ユーザー登録機能(JWTによるトークン認証)
-
開発環境
- MacOS Mojave
- Ruby(2.5.1)
- Ruby on Rails(5.2.1)
- Vue.js(2.6.10)
- Yarn(1.17.0)
- Webpack(4.39.2)
-
学習情報URL
<本文>
■ 前回分の修正
前回のビューはERBで実装したが,今後はSPAによる実装を進めるため,ERBによる実装を削除し,ERBによるViewをVue.jsに置き換える作業を実施する。
○1:[App.vue]にビューの情報を転記
<template>
<div class="container">
<h1 class="#f3e5f5 purple lighten-5 center">[Rails+Vue.js]~Bookshelf~</h1>
<div class="row #e3f2fd blue lighten-5">
<div class="col s4 m6" v-for="book in books">
<div class="card btn">
<span class="card-title" v-on:click="setBookInfo(book.id)">
{{ book.title }}
</span>
</div>
</div>
</div>
<div class="row" v-show="bookInfoBool">
<div class="col s12 m12">
<div class="card blue-grey darken-1">
<div class="card-content white-text">
<span class="card-title">
【{{ bookInfo.title }}】
</span>
<div class="detail">
・著者:{{ bookInfo.author }}
</div>
<div class="detail">
・出版社:{{ bookInfo.publisher }}
</div>
<div class="detail">
・ジャンル:{{ bookInfo.genre }}
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Vue from 'vue'
import axios from 'axios'
export default {
name: 'BookHome',
data: function() {
return {
bookInfo: {},
bookInfoBool: false,
books: [],
}
},
mounted: function() {
this.fetchBooks();
},
methods: {
fetchBooks() {
axios.get('/api/books').then((res) => {
for(var i = 0; i < res.data.books.length; i++) {
this.books.push(res.data.books[i]);
}
}, (error) => {
console.log(error);
});
},
setBookInfo(id){
axios.get(`api/books/${id}.json`).then(res => {
this.bookInfo = res.data;
this.bookInfoBool = true;
});
}
}
}
</script>
<style scoped></style>
-
Rubyのコードも含まれているため,該当部分を次の事項のとおり修正
-
[v-for="book in books"]
- [books]に格納された各情報について,[book]にそれぞれ格納し,定義した要素内で展開する。
- Rubyだと[books.each { |book| book.title }]と同値である。
-
[mounted: function(){}]
- Vueインスタンスのマウント直後に実行される。
-
[fetchBooks(){}]
- [axios.get('/api/books')]により,Railsから情報を受け取り,
- [for(var i = 0; i < res.data.books.length; i++) { this.books.push(res.data.books[i]) }]で,受け取った情報をbooksに順次格納する。
-
参考URL
○2:APIの作成
class Api::BooksController < ApplicationController
def index
@books = Book.all
render 'index', formats: 'json', handlers: 'jbuilder'
end
...
end
json.set! :books do
json.array! @books do |book|
json.extract! book, :id, :title
end
end
○3:ルーティングの修正
Rails.application.routes.draw do
root to: 'home#index'
namespace :api do
resources :books, only: [:index, :show]
end
end
- ERBのルーティングを削除
○4:動作確認
- ここまでで,前回と同じ動作になると成功です。
■ Vue Routerの設定
○1:Vue Routerを導入
Vue Routerにより,登録されたパスとコンポーネントで,画面内を差し替えること可能にするSPAの要となるライブラリである。
$ yarn add vue-router
○2:[App.vue]を修正
<template>
<div id="app">
<router-view/>
</div>
</template>
<script>
export default {
name: 'App',
}
</script>
<style scoped></style>
- [router-view]:router.jsで登録されたコンポーネントが表示される場所
- [name: 'App']:VueDevtoolsにおけるコンポーネントの識別名称
- 参考URL
○3:[router.js]を作成
import Vue from 'vue'
import VueRouter from 'vue-router'
import BookHome from '../pages/BookHome.vue'
Vue.use(VueRouter)
const routes = [
{ path: '/', name: 'BookHome', component: BookHome },
];
export default new VueRouter({ routes });
○4:[application.js]を修正
import Vue from 'vue'
import App from './App.vue'
import Router from '../router/router.js'
const app = new Vue({
el: '#app',
router: Router,
render: h => h(App)
})
○5:[BookHome.vue]を作成
<template>
<div class="container">
<h1 class="#f3e5f5 purple lighten-5 center">[Rails+Vue.js]~Bookshelf~</h1>
<div class="row #e3f2fd blue lighten-5">
<div class="col s4 m6" v-for="book in books">
<div class="card btn">
<span class="card-title" v-on:click="setBookInfo(book.id)">
{{ book.title }}
</span>
</div>
</div>
</div>
<div class="row" v-show="bookInfoBool">
<div class="col s12 m12">
<div class="card blue-grey darken-1">
<div class="card-content white-text">
<span class="card-title">
【{{ bookInfo.title }}】
</span>
<div class="detail">
・著者:{{ bookInfo.author }}
</div>
<div class="detail">
・出版社:{{ bookInfo.publisher }}
</div>
<div class="detail">
・ジャンル:{{ bookInfo.genre }}
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'BookHome',
data: function() {
return {
bookInfo: {},
bookInfoBool: false,
books: [],
}
},
mounted: function() {
this.fetchBooks();
},
methods: {
fetchBooks() {
axios.get('/api/books').then((res) => {
for(var i = 0; i < res.data.books.length; i++) {
this.books.push(res.data.books[i]);
}
}, (error) => {
console.log(error);
});
},
setBookInfo(id){
axios.get(`api/books/${id}.json`).then(res => {
this.bookInfo = res.data;
this.bookInfoBool = true;
});
}
}
}
</script>
<style scoped></style>
- 修正前の[App.vue]をそのまま転記
- [router-view]に差し込まれるコンポーネント
■ Headerの設定
次の記事で本の登録機能を作成するため,ここでルーティング用のヘッダーを作成しておく。
○1:[components]フォルダ及び[Header.vue]を作成
<template>
<div>
<nav>
<div class="nav-wrapper">
<router-link to="/" class="brand-logo">Bookshelf</router-link>
<ul id="nav-mobile" class="right">
<li><router-link to="">本の登録</router-link></li>
</ul>
</div>
</nav>
</div>
</template>
<script></script>
<style scoped></style>
○2:[App.vue]を修正
<template>
<div id="app">
<Header/>
<router-view/>
</div>
</template>
<script>
import Header from '../components/Header.vue'
export default {
name: 'App',
components: {
Header
},
}
</script>
<style scoped></style>
■ 不要データの削除
- [app/javascript/packs/book.js]:削除
- [app/views/books]:削除
- [app/controllers/books_controller.rb]:削除
〜Part3:Vue Router設定編終了〜
〜Part4:Create, Update, Delete編へ進む〜