前回の記事の続きになります。
前回の記事はこちら↓
本記事の概要
今回実装する機能について
【ローディング表示機能】
- vue-loading-template を導入してローディングを表示させます。
【ページネーション機能】
- gem で kaminari を導入して実装します。
【ユーザー管理機能】
- 今回は devise で実装します。
記事構成
本アプリケーションについての記事は大きく4部構成で作成しております。
- 第一章:アプリケーション作成〜ダミーデータ表示
- 第二章:ブックマークのCRUD実装
- 第三章:フリーワード検索、カテゴリー別絞り込み機能の実装
- 第四章:本記事(ローディング表示、ページネーション、ユーザー管理機能の実装)
本記事の参考URL
本記事の作成にあたって主に以下のドキュメント・記事を参考にさせて頂きました。
- 【Vue.js】ローディング画面の実装方法(サンプルコード付き)
- vue-loading (Github)
- 【rails】kaminariを使ってページネーションを作る
- Rails + Vue.js でページネーション付きのテーブルを簡単作成
実装
それでは実装に移ります。
ローディング表示機能
vue-loading-templateを導入して実装します。
参考記事↓
% yarn add vue-loading-template
app.vueを編集
<template>
<v-app id="app">
<!-- ローディングを表示 -->
<Loading v-show="isLoading"></Loading>
<v-container>
<!-- 中略 -->
</v-container>
</v-app>
</template>
<script>
import draggable from 'vuedraggable'
import Loading from './components/Loading' // Loading コンポーネントをインポート
import axios from 'axios';
export default {
data: function () {
return {
isLoading: true,
bookmarkList: ['',''],
allData: ['',''],
...
}
},
mounted () {
axios.get("/api/bookmarks")
.then(response => {
this.allData = response.data;
this.isLoading = false; // マウントしたらfalseに変更
}
);
this.setBookmark();
},
components: {
Loading, // Loagingコンポーネントを追加
draggable,
},
methods: {
...
}
}
</script>
components フォルダ配下に Loading.vue を設置して実装
<template>
<div v-show="isLoading">
<div class="fullview">
<div class="loading-spacer"></div>
<!-- spiningDubblesというタイプのローディングを実装 -->
<vue-loading
type="spiningDubbles"
color="#2f8af0"
:size="{ width: '100px', height: '100px' }"
>
</vue-loading>
</div>
</div>
</template>
<script>
import { VueLoading } from 'vue-loading-template'
export default {
name: 'isLoading',
data() {
return {
isLoading: true,
}
},
components: {
VueLoading,
},
}
</script>
<style>
.fullview {
width: 100%;
height: 100%;
background: rgba(254, 254, 254, 0.8);
position: fixed;
top: 60px;
left: 0;
z-index: 5;
}
.loading-spacer {
height: 30%;
}
</style>
以上でローディング表示機能の実装は終了です。
ページネーション機能
kaminariを導入して実装します。
参考記事↓
gem 'kaminari'
% bundle install
bookmarks_controllerの編集
class Api::BookmarksController < ApplicationController
def index
page = params[:page] || 1 # 表示するページの指定
per = params[:per] || 30 # 1ページあたり30件のブックマークを表示
@bookmarks = Bookmark.order('created_at DESC').page(page).per(per)
total_pages = @bookmarks.total_pages # 全ページ数を取得
response = {
bookmarks: @bookmarks,
total_pages: total_pages,
}
render json: response
end
end
app.vueの編集
【 Vue.js の編集】
<script>
export default {
data: function () {
return {
isLoading: true,
bookmarkList: ['',''],
...
currentPage: 1, // 表示するページの番号
itemsPerPage: 30, // 1ページあたりの表示件数
totalPages: null, // ページネーションした時の全ページ数
}
},
...
methods: {
setBookmark: function () {
// Axios で api にリクエスト
const url = `/api/bookmarks?page=${this.currentPage}?per=${this.itemsPerPage}`;
axios.get(url)
.then(response => {
// ブックマークの全データを取得
this.allData = response.data.bookmarks
this.bookmarkList = this.allData
// ページネーションの全ページ数を取得
this.totalPages = response.data.total_pages
this.listCategories();
this.abstruct();
}
);
},
...
}
}
</script>
【template の編集】
<template>
<v-app id="app">
<Loading v-show="isLoading"></Loading>
<v-container style="height: 1000px; max-width: 2400px; padding: 0 20px;">
<v-layout>
<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;">
<draggable v-model="bookmarkList" style="margin: 0 25px; width: 80%; cursor: pointer;">
<v-card v-for="bookmark in bookmarkList" :key="bookmark.id" :items-per-page="itemsPerPage" 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%">
<a v-bind:href="bookmark.url" target="_blank" rel="noopener noreferrer" style="font-size: 18px;">
{{ bookmark.title }}
</a>
<v-tooltip right>
<template v-slot:activator="{ on }">
<v-btn light v-on="on" @click="togglePutModal(bookmark.id)" style="margin-bottom: 8px">
<span class="material-icons" style="margin-right: 4px;">create</span>
</v-btn>
</template>
<span>編集する</span>
</v-tooltip>
</div>
<v-divider></v-divider>
<div style="font-size: 16px; display: flex; justify-content: space-between; width: 100%">
<div>#{{ bookmark.category }}</div>
<v-tooltip right>
<template v-slot:activator="{ on }">
<v-btn dark v-on="on" @click="toggleDeleteModal(bookmark.id)" style="margin-top: 8px">
<span class="material-icons" style="margin-right: 4px;">delete</span>
</v-btn>
</template>
<span>削除する</span>
</v-tooltip>
</div>
</div>
</v-card-title>
</v-card>
</draggable>
</v-flex>
</v-layout>
<!-- ページネーション表示部分を追加 -->
<div class="text-xs-center" style="margin: 20px 0 40px 0;">
<v-pagination
v-model="currentPage"
:length="totalPages"
@input="setBookmark"
></v-pagination>
</div>
</v-flex>
</v-layout>
</v-container>
</v-app>
</template>
以上でページネーション機能の実装は終了です。
ユーザー管理機能の実装
deviseを導入して実装します。(新規登録ページやログインページの実装に関しては割愛します。)
本来は最初に実装するべきですが、今回はブックマークの機能(Vue.js)を主な目的としているため後から実装しています。
gem 'devise'
bundle install
してdeviseをインストール
% bundle install
% rails g devise:install
bookmarkのマイグレーションファイルをロールバックして編集
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
userモデルの作成
% rails g devise user
class DeviseCreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
## Database authenticatable
t.string :nickname, null: false, default: ""
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
t.timestamps null: false
end
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
end
end
% rails db:migrate
application_controller
の編集
class ApplicationController < ActionController::Base
# 登録に必要なストロングパラメータを設定
before_action :configure_permitted_parameters, if: :devise_controller?
# ユーザーがログインしていない場合はログインページを表示
before_action :authenticate_user!
private
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname])
end
end
それぞれのモデルを編集(アソシエーションの記述)
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :bookmarks # 一人のユーザーは複数のブックマークのデータを扱う
# バリデーション設定
validates :nickname, presence: true
PASSWORD_REGEX = /\A(?=.*?[a-z])(?=.*?[\d])[a-z\d]+\z/i.freeze
validates_format_of :password, with: PASSWORD_REGEX, message: 'には英字と数字の両方を含めて設定してください'
end
class Bookmark < ApplicationRecord
belongs_to :user
with_options presence: true do
validates :url, format: {with: /\A#{URI::regexp(%w(http https))}\z/}
end
end
bookmarks_controller
の編集
class Api::BookmarksController < ApplicationController
protect_from_forgery :except => [:create, :update, :destroy]
def index
page = params[:page] || 1
per = params[:per] || 30
# ログインしているユーザーのブックマーク情報を取得
@bookmarks = Bookmark.where(user_id: current_user.id).order('created_at DESC').page(page).per(per)
total_pages = @bookmarks.total_pages
response = {
bookmarks: @bookmarks,
total_pages: total_pages,
}
render json: response
end
end
以上でユーザー管理機能の実装は終了です。
また、今後学習を深めてJsonWebToken(JWT)によるユーザー認証機能の実装にも挑戦したいと思います。
最後に
以上でブックマーク個人管理アプリケーションの実装は全て終了です。
いかがだったでしょうか?
未だ記事を書くことに慣れていない為分かりづらい部分や読みにくい部分があるかもしれませんが今後修正点や改善点を気付き次第修正をしていこうと思います。
読んでいただき、ありがとうございました。