40
40

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.

Rails + Vue.js でページネーション付きのテーブルを簡単作成

Last updated at Posted at 2020-05-18

概要

  • 業務でページネーション機能を実装したので、ほとんどそのままの構成で手順を紹介
  • 使用した技術は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を編集しサンプルデータを作成

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

Gemfile
# kaminariを追記
gem 'kaminari'
# kaminariのインストール。インストール完了後にサーバーを再起動させましょう
bundle

app/controllers/api/books_controller.rbを作成し、Book一覧を返すapiを実装します

app/controllers/api/books_controller.rb
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
config/routes.rb
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が返ってきます

スクリーンショット 2020-05-18 22.22.11.png

Book一覧表示ページの作成

Book一覧表示用のページを作成します
Vuetifyのv-data-tableコンポーネントv-paginationを使い、Axiosでapiを叩きます
※ ここでは面倒を避けるためCDN経由で環境構築をしてあります。適宜ご自身の環境に合わせた環境構築を行なってください

app/views/layouts/application.html.erb
<!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>
app/views/books/index.html.erb
<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です!

スクリーンショット 2020-05-18 23.38.17.png スクリーンショット 2020-05-18 23.38.27.png
40
40
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
40
40

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?