13
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Rails6 + Vue.js】ブックマークを管理する簡単なSPAを作ってみた話(第一章:アプリ作成〜ダミーデータ表示)

Last updated at Posted at 2021-04-01

先日ドットインストールで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を導入したユーザー管理機能についての説明は割愛します。

概要

完成版イメージ

トップページの見た目はこのような感じです。
Image from Gyazo

実装機能

  • ブックマーク一覧表示
  • ブックマーク新規投稿
  • ブックマーク編集
  • ブックマーク削除
  • ブックマーク並び替え
  • フリーワード検索
  • カテゴリー別検索
  • ローディング表示
  • ページネーション(kaminari)
  • ユーザー管理(devise)

記事構成

本アプリケーションについての記事は大きく4部構成で作成します。

本記事の参考URL

本記事の作成にあたって以下の記事を参考にさせて頂きました。

構築手順(アプリ作成〜ダミーデータ表示)

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アクションをページのルーティングとして設定します。

config/routes.rb
Rails.application.routes.draw do
  root to: 'home#index'
end

コントローラの作成

上記でルーティングで設定したhomeコントローラーとindexのビューを作成します。

% rails g controller home index
app/controlllers/home_controller.rb
class HomeController < ApplicationController
  def index
  end
end

作成された app/views/home/index.html.erbapp/javascript/packs/hello_vue.js ファイルを読み込むコードを記載します。これは、hello_vue.js ファイルを介してjavascript/packs/app.vueファイルを読み込みための記述です。

app/views/home/index.html.erb
<%= javascript_pack_tag 'hello_vue' %>

この時点でサーバーを立ち上げ、http://localhost:3000/home にアクセスすると画面に「Hello Vue!」が表示されます。

モデルについて

今回のアプリケーションに実装するモデルはUserモデルとBookmarkモデルの2つです。
しかし、Usersテーブルを先に作ってしまうと処理の確認等開発の効率が悪くなると考えたため自分はまずBookmarkモデルを作成し一通りの処理を完成させてからUsersモデルをdeviseで実装しました。
本記事でも最初にBookmarkモデルを作り、アプリの主要な機能の実装が完了してからUserモデルの実装を進めることにします。

【ER図】
Image from Gyazo

Bookmarkモデルの作成

Bookmarkモデルを作成します。

% rails g model Bookmark

Bookmarkテーブルにはそれぞれタイトル、URL、カテゴリーを保存すると設計したため、作成されたマイグレーションファイルに以下の設定を記述します。

Column Type Options
title string null: false
url string null: false
category string null: false
db/migrate/xxxxxx_create_bookmarks.rb
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が入力されていなければ保存できないように設定しました。

app/models/bookmark.rb
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

db/seeds.rb
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コントローラーのルーティングを設定

config/routes.rb
Rails.application.routes.draw do
  root to: 'home#index'

  namespace :api, format: 'json' do
    resources :bookmarks, only: [:index]
  end
end

作成したコントローラーでBookmarksテーブルからデータを取得
作成時間順で新しい投稿が上に来るように取得します。

app/controllers/api/bookmarks_controller.rb
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'のようにシンボルではなくシングルクオーテーションで書いてしまうとエラーになってしまうようです。

参考:レイアウトとレンダリング -Railsガイド

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

作成したファイルに以下のコードを記述します。

app/views/api/bookmarks/index.json.jbuilder
json.array! @bookmarks, :id, :title, :url, :category

コントローラーから渡されてきたインスタンス変数@bookmarksとキー:id, :title, :url, :categoryarray!メソッドを使って配列を回しデータを返します。

一覧表示させるだけなら: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を読み込むために以下のコードを記述します。

app/views/home/index.html.erb
<%= javascript_pack_tag 'top_vue' %>
<%= stylesheet_pack_tag 'top_vue'  %>

top_vue.jsで先程導入したVuetifyを読み込ませます。

app/javascript/packs/top_vue.js
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ファイルを確認します。

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属性で指定します。

app/javascript/app.vue
<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>

以上で本記事【アプリ作成〜ダミーデータ表示】は終了です。

次章はこちら↓

13
23
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
13
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?