概要
- 業務でページネーション機能を実装したので、ほとんどそのままの構成で手順を紹介
- 使用した技術は
kaminari(Rails)
とVuetify(Vue.js)
- api経由でデータを取得し、ページネーション付きで表示する
- ソースコード
Railsの開発環境は特に説明しないが、以下の記事を参考に構築しました
Rails 6 + MySQL on Dockerの環境を秒速で構築する
Bookモデルの定義とサンプルデータの作成
今回はデータベースに保存したBookの一覧をapiで取得します
まずはBookモデルを作成しましょう
# マイグレーションファイルの作成
bin/rails g model Book name:string
# マイグレーションを実行し、Booksテーブルを作成
bin/rails db:migrate
db/seeds.rb
を編集しサンプルデータを作成
100.times do |n|
name = "example-#{n+1}"
Book.create!(name: name)
end
seedを実行
bin/rails db:seed
これでBookレコードが100件作成されました
kaminariのインストールとBook一覧取得用apiの作成
Bookテーブルからデータを取得する際にkaminariを使用します
kaminariをインストールします
https://github.com/kaminari/kaminari
# kaminariを追記
gem 'kaminari'
# kaminariのインストール。インストール完了後にサーバーを再起動させましょう
bundle
app/controllers/api/books_controller.rb
を作成し、Book一覧を返すapiを実装します
class Api::BooksController < ApplicationController
def index
# 表示するページの番号を指定
page = params[:page] || 1
# 1ページあたりの表示件数を指定
per = params[:per] || 10
# ページネーションで指定レコードを取得
books = Book.page(page).per(per)
# ページネーションした時の全ページ数
total_pages = books.total_pages
# レスポンスデータの定義
response = {
# bookレコードはidとnameフィールドのみ表示する
books: books.select(:id, :name),
total_pages: total_pages
}
# json形式でレスポンスを返却
render json: response
end
end
Rails.application.routes.draw do
# Book一覧取得用のパス
get '/api/books', to: 'api/books#index'
# Book一覧表示用のパス
get '/books', to: 'books#index'
end
http://localhost:3000/api/books
にアクセスすると次のようなjsonが返ってきます
Book一覧表示ページの作成
Book一覧表示用のページを作成します
Vuetifyのv-data-tableコンポーネント
とv-pagination
を使い、Axiosでapiを叩きます
※ ここでは面倒を避けるためCDN経由で環境構築をしてあります。適宜ご自身の環境に合わせた環境構築を行なってください
<!DOCTYPE html>
<html>
<head>
<title>AppName</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%# CDNで Vue.js, Vuetify, Axios をインストールする %>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<%# ここも追加 %>
<script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.min.js"></script>
<body>
<%= yield %>
</body>
</html>
<div id="app">
<v-app>
<v-container>
<h2>Book一覧</h2>
<%# テーブル作成用コンポーネント %>
<v-data-table
:headers="headers"
:items="items"
:items-per-page="itemsPerPage"
hide-default-footer
/>
</v-container>
<%# ページネーション表示用コンポーネント %>
<v-pagination
v-model="currentPage"
:length="totalPages"
<%# ページを変更した時にfetchBooksを呼び出す %>
@input="fetchBooks"
/>
</v-ap>
</div>
<script>
new Vue({
el: "#app",
vuetify: new Vuetify(),
data() {
return {
// テーブルのヘッダー情報。valueの値がレコードのフィールド名に紐付く
headers: [
{ text: "ID", value: "id"},
{ text: "本の名前", value: "name"},
],
// テーブルのボディー情報。apiで取得したBook一覧をここに格納する
items: [],
// 表示するページの番号
currentPage: 1,
// 1ページあたりの表示件数
itemsPerPage: 10,
// ページネーションした時の全ページ数
totalPages: null,
}
},
methods: {
// AxiosでBook取得apiにリクエストを送る
fetchBooks() {
const url = `/api/books?page=${this.currentPage}?per=${this.itemsPerPage}`;
axios
.get(url)
.then(res => {
// Book一覧を取得
this.items = res.data.books;
// ページネーションした時の全ページ数を取得
this.totalPages = res.data.total_pages;
})
}
},
// DOMが作成された時に fetchBooks を呼び出す
created() {
this.fetchBooks()
},
});
</script>
http://localhost:3000/books
これでページネーションは完成です
以下のようにページを切り替える度に表示が変わればOKです!