Rails+Vue.js+Webpackerによる「Create, Read, Update, Destroy」処理のチュートリアルを記述する。
なお,前提知識として,Railsチュートリアル終了程度を想定する。
<概要>
■ 本記事の内容
- ViewをERB,API通信をVue.jsにより,本の詳細情報に係る表示機能を実装する。
- 今回のコードは,GitHubのコミット履歴で確認可能である。
- [Bookモデル及びデータベースを生成]から[APIを使用したRead機能を実装]まで
■ 記事共通
-
目次
-
実装機能
- お気に入りの本に係る登録,参照,編集,削除処理
- 非同期通信(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
<本文>
■ Bookモデル及びデータベースを生成
.bash
$ rails g model Book title:string author:string publisher:string genre:string
db/migrate/[time_stump]_create_books.rb
class CreateBooks < ActiveRecord::Migration[5.2]
def change
create_table :books do |t|
t.string :title, null: false
t.string :author, null: false
t.string :publisher, null: false
t.string :genre, null: false
t.timestamps
end
end
end
app/models/book.rb
class Book < ApplicationRecord
validates :title, presence: true, length: { maximum: 100 }
validates :author, presence: true, length: { maximum: 100 }
validates :publisher, presence: true, length: { maximum: 50 }
validates :genre, presence: true, length: { maximum: 50 }
end
.bash
$ rails db:migrate
- 本の登録情報として,タイトル,著者,出版社,ジャンルを登録する。
- テーブルにバリデーションを設定しておく。
- 参考URL
■ 本のサンプルデータを作成
○1: Gem[faker]をGemfileに追加
Gemfile
...
gem 'materialize-sass'
gem 'material_icons'
gem 'faker'
...
.bash
$ bundle install
○2: サンプルデータを登録
config/seeds.rb
20.times do
Book.create!(
title: Faker::Book.title,
author: Faker::Book.author,
publisher: Faker::Book.publisher,
genre: Faker::Book.genre
)
end
.bash
$ rails db:migrate
- ※追記:seeds.rbでデータ投入する際、createよりもcreate!を使うべき?
- [rails db:seed]で登録失敗時に例外処理が戻り,エラーの検知が容易になる。
■ Booksコントローラを生成し,本情報を表示
.bash
$ rails g controller Books index
config/routes.rb
Rails.application.routes.draw do
root to: 'home#index'
resources :books, only: [:index]
end
app/controllers/books_controller.rb
class BooksController < ApplicationController
def index
@books = Book.all
end
end
app/views/books/index.html.erb
<div class="container">
<h1 class="#f3e5f5 purple lighten-5 center">[Rails+Vue.js]~Bookshelf~</h1>
<div class="row #e3f2fd blue lighten-5">
<% @books.each do |book| %>
<div class="col s4 m6">
<div class="card">
<span class="card-title">
<%= book.title %>
</span>
</div>
</div>
<% end %>
</div>
</div>
■ APIの生成
○1: APIの送信元を作成
.bash
$ rails g controller api::books
config/routes.rb
Rails.application.routes.draw do
...
namespace :api do
resources :books, only: [:show]
end
end
app/controllers/api/books_controller.rb
class Api::BooksController < ApplicationController
def show
@book = Book.find(params[:id])
render 'show', formats: 'json', handlers: 'jbuilder'
end
end
app/views/api/books/show.json.jbuilder
json.id @book.id
json.title @book.title
json.author @book.author
json.publisher @book.publisher
json.genre @book.genre
# 次のコードと同等です。
# json.extract! @book, :id, :title, :author, :publisher, :genre
○2: 実装を確認
{"id":1,"title":"ヤマノススメ","author":"しろ","publisher":"アース・スター エンターテイメント","genre":"アウトドア"}
- http://localhost:5000/api/books/1.json で上記のように表示されたら成功です。
■ API通信を実現するライブラリを導入
.bash
$ yarn add axios
- 参考URL
■ APIを使用したRead機能を実装
○1: Vue.js側のエントリーポイントを作成
app/javascript/packs/book.js
import Vue from 'vue/dist/vue.esm';
import axios from 'axios';
new Vue({
el: '.js-booksIndex',
data: {
bookInfo: {},
bookInfoBool: false
},
methods: {
setBookInfo(id){
axios.get(`api/books/${id}.json`).then(res => {
this.bookInfo = res.data;
this.bookInfoBool = true;
});
}
}
});
- [el: '.js-booksIndex']:book.jsの対象範囲
- [data.bookInfo]:表示する本の詳細情報
- [data.bookInfoBool]:情報の表示有無を判定(デフォルトで非表示設定)
- [setBookInfo(id):詳細情報を表示するメソッド
- [axios.get(
api/books/${id}.json
)]により,APIを受け取り, - [this.bookInfo = res.data;]により,本情報をdataに格納し,
- [this.bookInfoBool = true;]により,非表示を表示に変更して,詳細情報が表示される。
- [axios.get(
○2: Rails側のViewを作成
app/views/books/index.html.erb
+ <div class="container js-booksIndex">
<h1 class="#f3e5f5 purple lighten-5 center">[Rails+Vue.js]~Bookshelf~</h1>
<div class="row #e3f2fd blue lighten-5">
<% @books.each do |book| %>
<div class="col s4 m6">
+ <div class="card btn">
+ <span class="card-title" v-on:click="setBookInfo(<%= book.id %>)">
<%= book.title %>
</span>
</div>
</div>
<% end %>
</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>
+ <%= javascript_pack_tag 'book' %>
- [v-on:click="setBookInfo(<%= book.id %>)]
- [v-on:click="~"]により,クリックした際の動作を指定する。
- この場合,[book.js]の[setBookInfo(id)]が動作して,[data.bookInfo]にクリックした本の情報が格納される。
- なお,[v-on:click]は,[@click]で省略可能である。
- [v-show="bookInfoBool"]
- [v-show="~"]により,中身がTrueの場合,要素が表示される。
- この場合,[book.jsの[data.bookInfoBool]が参照され,表示有無が判定される。
- 参考URL
- 上記のように動作したら成功である。
- なお,現在のビューは,ERBを使用しているが,SPAによる実装を目指すため,次の記事で全てVueのコンポーネントに置き換える。
〜Part2:Read編終了〜
〜Part3:Vue Router設定編へ進む〜