先日ドットインストールでVue.jsの学習を一通り終え、何かアウトプットとして個人でSPAを開発したいと考え、Ruby on Rails + Vue.jsで CRUD と検索機能等を持った簡単なブックマークの個人管理アプリケーションを作成したので構築手順や開発中に調べて気づいたことなどを記事にまとめます。
前提条件
【バージョンについて】
- Ruby 2.6.5
- Ruby on Rails 6.0.0
- yarn 1.22.5
- Vue.js 2.6.12
- Vuetify 2.4.7
DBはMySQLを使用
【記事内の説明について】
HTML、CSS、Vuetifyの説明は割愛します。
また、今回はVue.jsで初のCRUDを実装することを主な目的としているため、deviseを導入したユーザー管理機能についての説明は割愛します。
概要
完成版イメージ
実装機能
- ブックマーク一覧表示
- ブックマーク新規投稿
- ブックマーク編集
- ブックマーク削除
- ブックマーク並び替え
- フリーワード検索
- カテゴリー別検索
- ローディング表示
- ページネーション(kaminari)
- ユーザー管理(devise)
記事構成
本アプリケーションについての記事は大きく4部構成で作成します。
- 第一章:本記事(アプリケーション作成〜ダミーデータ表示)
- 第二章:非同期によるブックマークの新規投稿、編集、削除、並び替え機能の実装
- 第三章:フリーワード検索、カテゴリー別絞り込み機能の実装
- 第四章:ローディング表示、ページネーション、ユーザー管理機能の実装
本記事の参考URL
本記事の作成にあたって以下の記事を参考にさせて頂きました。
- [Rails+Vue.js]に係るCRUD入門〜Part1:環境構築編〜
- 【Rails6】10分でRails + Vue + Vuetifyの環境を構築する
- 既存のRailsアプリにVue.jsを導入した
構築手順(アプリ作成〜ダミーデータ表示)
Railsアプリケーションを作成
アプリの名前はbookmark-appで作成します。
% rails _6.0.0_ new bookmark-app -d mysql
% cd bookmark-app
% bundle install
% rails db:create
Webpackerをインストール
Webpackerは、複数のJavaScriptファイルなどを1つにまとめて出力するツールであるwebpackを簡単にRailsアプリに統合できるGemであり、Rails5.1以降標準でサポートされています。
Gemは既に追加されているため、こちらをターミナルでインストールします。
% rails webpacker:install
Vue.jsの導入
Vue.jsをWebpackerで導入するために以下のコマンドを実行します。
% rails webpacker:install:vue
ルーティングの設定
今回はhomeコントローラーのindexアクションをページのルーティングとして設定します。
Rails.application.routes.draw do
root to: 'home#index'
end
コントローラの作成
上記でルーティングで設定したhomeコントローラーとindexのビューを作成します。
% rails g controller home index
class HomeController < ApplicationController
def index
end
end
作成された app/views/home/index.html.erb
に app/javascript/packs/hello_vue.js
ファイルを読み込むコードを記載します。これは、hello_vue.js ファイルを介してjavascript/packs/app.vueファイルを読み込みための記述です。
<%= javascript_pack_tag 'hello_vue' %>
この時点でサーバーを立ち上げ、http://localhost:3000/home にアクセスすると画面に「Hello Vue!」が表示されます。
モデルについて
今回のアプリケーションに実装するモデルはUserモデルとBookmarkモデルの2つです。
しかし、Usersテーブルを先に作ってしまうと処理の確認等開発の効率が悪くなると考えたため自分はまずBookmarkモデルを作成し一通りの処理を完成させてからUsersモデルをdeviseで実装しました。
本記事でも最初にBookmarkモデルを作り、アプリの主要な機能の実装が完了してからUserモデルの実装を進めることにします。
Bookmarkモデルの作成
Bookmarkモデルを作成します。
% rails g model Bookmark
Bookmarkテーブルにはそれぞれタイトル、URL、カテゴリーを保存すると設計したため、作成されたマイグレーションファイルに以下の設定を記述します。
Column | Type | Options |
---|---|---|
title | string | null: false |
url | string | null: false |
category | string | null: false |
class CreateBookmarks < ActiveRecord::Migration[6.0]
def change
create_table :bookmarks do |t|
t.string :title, null: false
t.string :url, null: false
t.string :category, null: false
t.references :user, foreign_key: true
t.timestamps
end
end
end
記述が完了し保存したらマイグレーションを実行します。
% rails db:migrate
次に、Bookmarkモデルにバリデーションを設定します。
タイトル、URL、カテゴリーは全て空のデータが保存されないようにし、タイトルとカテゴリーの文字数は最大50文字、URLは正しいURLが入力されていなければ保存できないように設定しました。
class Bookmark < ApplicationRecord
validates :title, presence: true, length: { maximum: 50 }
validates :category, presence: true, length: { maximum: 50 }
with_options presence: true do
validates :url, format: {with: /\A#{URI::regexp(%w(http https))}\z/}
end
end
以上で、Bookmarkモデルの作成は完了です。
ダミーデータの作成(Fakerの導入)
ブックマークの一覧表示機能の実装にあたって、あらかじめデータがちゃんと表示されるかどうかを確認する初期データが必要です。
手動でデータを挿入してもいいのですが手間がかかるため、今回はGemでFakerを導入して簡単にダミーデータを作成しそちらを表示させます。
まず、GemfileにFakerを記載
gem 'faker'
そして、ターミナルでbundle installを実行します。
% bundle install
ダミーデータの作成(seeds.rbの編集)
seedファイルは、データベースにあらかじめ入れておきたいデータを定義しておくものです。
データをあらかじめ定義することで、コマンドで簡単にDBへデータを投入できるため、先程導入したFakerを使ってダミーデータを作成しbookmarksテーブルにデータを入れていきます。
参考URL : faker-ruby
10.times do
Bookmark.create(
title: Faker::Lorem.word,
url: Faker::Internet.url,
category: Faker::Lorem.word,
)
end
上記の記述でタイトル、URL、カテゴリーそれぞれにFakerでダミーデータを作成し、それを繰り返し処理で10個分のブックマークのデータを作成しています。
記載が完了したらターミナルでseeds.rbファイルを実行します。
% rails db:seed
以上でダミーデータの作成は完了です。
Bookmarksコントローラーの作成(API)
作成したダミーデータを取得してデータを渡すためにAPIの送信元を作成します。
% rails g controller api::bookmarks
Bookmarksコントローラーのルーティングを設定
Rails.application.routes.draw do
root to: 'home#index'
namespace :api, format: 'json' do
resources :bookmarks, only: [:index]
end
end
作成したコントローラーでBookmarksテーブルからデータを取得
作成時間順で新しい投稿が上に来るように取得します。
class Api::BookmarksController < ApplicationController
def index
@bookmarks = Bookmark.order('created_at DESC')
render "index", formats: :json, handlers: "jbuilder"
end
end
因みにRails6以降では、formats: :json
の部分をformats: 'json'
のようにシンボルではなくシングルクオーテーションで書いてしまうとエラーになってしまうようです。
jbuilderでjsonを返す
APIとしてデータを返す時、ほとんどの場合json形式でデータを渡すことが多いです。
Railsには、そのjsonの作成を簡単にしてくれるjbuilderという機能があります。
そして今回はそのjbuilderを使って、bookmarksコントローラーからAPIとしてデータを返していきます。
まずapp/views/api/bookmarks
配下にindex.json.jbuilder
ファイルを作成します。
% touch app/views/api/bookmarks/index.json.jbuilder
作成したファイルに以下のコードを記述します。
json.array! @bookmarks, :id, :title, :url, :category
コントローラーから渡されてきたインスタンス変数@bookmarks
とキー:id, :title, :url, :category
をarray!
メソッドを使って配列を回しデータを返します。
一覧表示させるだけなら:id
は必要ないのですが、今回はCRUDを実装するためIDも渡します。
以上でサーバーサイドの実装はひとまず完成です。
axiosの導入
APIの非同期通信を行うために、それを実現するライブラリのaxiosを導入します。
参考:axios を利用した API の使用ー Vue.js
% yarn add axios
Vuetifyの導入
Vue.jsのUIライブラリであるVuetifyを導入します。
Vuetify
% yarn add vuetify
ここで今回自分はVue.jsを導入した時デフォルトで生成されたapp/javascript/packs/hello_vue.js
ファイルを削除し、新たにapp/javascript/packs/top_vue.js
を作成して実装しました。
コードをスッキリさせるためです。
hello_vue.js
を削除したのでindex.html.erb
の内容も忘れずに変更。
app/javascript/packs/top_vue.js
を読み込むために以下のコードを記述します。
<%= javascript_pack_tag 'top_vue' %>
<%= stylesheet_pack_tag 'top_vue' %>
top_vue.js
で先程導入したVuetifyを読み込ませます。
import Vue from 'vue'
import Vuetify from "vuetify";
import "vuetify/dist/vuetify.min.css";
import App from '../app.vue'
Vue.use(Vuetify);
const vuetify = new Vuetify();
document.addEventListener('DOMContentLoaded', () => {
const app = new Vue({
vuetify,
render: h => h(App)
}).$mount()
document.body.appendChild(app.$el)
console.log(app)
})
以上でVuetifyの導入は完了です。
参考にさせて頂いた記事:【Rails6】10分でRails + Vue + Vuetifyの環境を構築する
ダミーデータの一覧表示機能の実装
いよいよ上で作成したダミーデータを表示させます。
まずはVue.js導入時にデフォルトで生成されているapp/javascript/app.vue
ファイルを確認します。
<template>
<div id="app">
<p>{{ message }}</p>
</div>
</template>
<script>
export default {
data: function() {
return {
message: "Hello Vue!"
};
}
};
</script>
<style scoped>
p {
font-size: 2em;
text-align: center;
}
</style>
今回このアプリケーションでは基本的にこのapp/javascript/app.vue
ファイルを編集してフロント機能の実装を進めていきます。
スタイルは全てstyle属性で指定します。
<template>
<v-app id="app">
<v-container style="height: 1000px; max-width: 2400px; padding: 0 20px;">
<v-layout>
<v-flex xs2 style="justify-content: center; padding: 20px 5px 0 5px">
<h3>サイドメニュー表示</h3>
</v-flex>
<v-flex xs8>
<div style="width: 100%; margin: 5px 0 20px 0; display: flex; justify-content: center;">
<h1>Bookmark 一覧</h1>
</div>
<v-layout>
<v-flex row wrap style="justify-content: center;">
<!-- v-forで bookmarkList の配列の中身を取り出してbookmarkに代入し繰り返す -->
<v-card v-for="bookmark in bookmarkList" :key="bookmark.id" style="width: 100%">
<v-card-title primary-title style="margin-bottom: 15px; width: 100%; padding-bottom: 10px;">
<div style="width: 100%;">
<div class="headline mb-0" style="display: flex; justify-content: space-between; width: 100%">
<p style="font-size: 18px;">
{{ bookmark.title }} <!-- タイトルを表示 -->
</p>
</div>
<v-divider></v-divider>
<div style="font-size: 16px; display: flex; justify-content: space-between; width: 100%">
<div>
#{{ bookmark.category }} <!-- カテゴリーを表示 -->
</div>
</div>
</div>
</v-card-title>
</v-card>
</v-flex>
</v-layout>
</v-flex>
<v-flex xs2>
<v-btn style="margin: 20px 0 40px 0;">
Bookmarkを追加する <!-- 追加するモーダルを表示させるボタン(後に実装) -->
</v-btn>
<p style="margin-right: 30px">- Bookmark List -</p>
<!-- 右側にもリストとしてブックマークをタイトルのみ一覧表示させる -->
<ul v-for="bookmark in bookmarkList" :key="bookmark.id" style="list-style: none; margin-right: 30px">
<li style="margin-top: 10px;">
<p>{{ bookmark.title }}</p> <!-- タイトルを表示させる -->
</li>
<hr>
</ul>
</v-flex>
</v-layout>
</v-container>
</v-app>
</template>
<script>
import axios from 'axios';
export default {
data: function () {
return {
bookmarkList: ['',''], // 空配列を用意
}
},
mounted () {
this.setBookmark(); // setBookmark() を呼び出す
},
methods: {
setBookmark: function () {
axios.get('/api/bookmarks') // axiosを使ってデータを取得
.then(response => {
this.bookmarkList = response.data // axiosで呼び出したAPIの情報をbookmarkListに代入
}
);
},
}
}
</script>
以上で本記事【アプリ作成〜ダミーデータ表示】は終了です。
次章はこちら↓