カテゴリー
Linux

【Vue.js 3】 Bootstrap4 ページネーションのシングルファイルコンポーネントを、参考サイトそのままに Vue.js 3 Compotision API 化したコード

はじめに

本投稿は、 【Vue.js】ページネーションコンポーネント(ページ遷移ごとにデータを取得するタイプ) | Wood-Roots:blog にて Vue.js 2 で書かれたページネーションのシングルファイルコンポーネント (SFC) を Vue.js 3 で書き直したものです。

元となった 【Vue.js】ページネーションコンポーネント(ページ遷移ごとにデータを取得するタイプ) | Wood-Roots:blog に感謝申し上げます!

また、本投稿は、本ブログの Vue.js 3 で flatpickr をラップした SFCその2 。ページ移動時に発生していたエラーを解消する – oki2a24 の続きとなります。

書いたコードの構成

  • laravel/resources/js/router/index.js: サンプルページを表示するためのルーティングを追加した。
  • laravel/resources/js/views/SamplePagination.vue: ページネーション SFC を利用する側のページ。
  • laravel/resources/js/components/BasePagination.vue: ページネーション SFC 本体。

コード

laravel/resources/js/router/index.js です。サンプルページを表示するためのルーティングを追加しました。差分を表示します。

$ git diff laravel/resources/js/router/index.js
diff --git a/laravel/resources/js/router/index.js b/laravel/resources/js/router/index.js
index bc9d99a..e159aa4 100644
--- a/laravel/resources/js/router/index.js
+++ b/laravel/resources/js/router/index.js
@@ -3,6 +3,7 @@ import ExampleComponent from "../components/ExampleComponent.vue";
 import SampleDropzone from "../views/SampleDropzone.vue";
 import SampleFlatpickr from "../views/SampleFlatpickr.vue";
 import SampleModal from "../views/SampleModal.vue";
+import SamplePagination from "../views/SamplePagination.vue";
 import SampleVueFlatpickr from "../views/SampleVueFlatpickr.vue";
 import SampleSelect2 from "../views/SampleSelect2.vue";
 import Vue3Dropzone from "../views/Vue3Dropzone.vue";
@@ -40,6 +41,11 @@ const routes = [
     name: "SampleModal",
     component: SampleModal,
   },
+  {
+    path: "/sample_pagination",
+    name: "SamplePagination",
+    component: SamplePagination,
+  },
   {
     path: "/sample_select2",
     name: "SampleSelect2",
$

laravel/resources/js/views/SamplePagination.vue: ページネーション SFC を利用する側のページです。

<template>
  <div class="container">
    <h1>SamplePagination</h1>
    <p>ページネーションを何件表示するか(showPages): {{ showPages }}</p>
    <p>現在のページ(currentPage): {{ currentPage }}</p>
    <p>総件数(totalCount): {{ totalCount }}</p>
    <p>総ページ数(totalPages): {{ totalPages }}</p>
    <p>1ページあたりの表示件数(perPage): {{ perPage }}</p>
    <hr />
    <p>クリックしたページ数: {{ clickedPageNumber }}</p>
    <base-pagination
      :show-pages="showPages"
      :current-page="currentPage"
      :total-count="totalCount"
      :total-pages="totalPages"
      :per-page="perPage"
      @currentPage="changePage"
    />
  </div>
</template>

<script>
import { ref } from "@vue/reactivity";
import BasePagination from "../components/BasePagination.vue";

export default {
  name: "SamplePagination",
  components: { BasePagination },
  setup() {
    const showPages = ref(10);
    const currentPage = ref(3);
    const totalCount = ref(111);
    const totalPages = ref(12);
    const perPage = ref(10);

    const clickedPageNumber = ref(0);

    const changePage = (pageNumber) => {
      clickedPageNumber.value = pageNumber;
    };

    return {
      changePage,
      clickedPageNumber,
      currentPage,
      perPage,
      showPages,
      totalCount,
      totalPages,
    };
  },
};
</script>

laravel/resources/js/components/BasePagination.vue: ページネーション SFC 本体です。

<template>
  <div v-if="totalPages" class="row py-3 justify-content-center">
    <div class="col-auto">
      <nav aria-label="Page navigation">
        <ul class="pagination">
          <li class="page-item" :class="{ disabled: currentPageEdited == 1 }">
            <a class="page-link" href="#" @click.prevent="setPage(1)"
              >&lt;&lt;</a
            >
          </li>
          <li class="page-item" :class="{ disabled: currentPageEdited == 1 }">
            <a
              class="page-link"
              href="#"
              :class="{ disable: currentPageEdited == 1 }"
              @click.prevent="setPage(currentPageEdited - 1)"
            >
              &lt;</a
            >
          </li>
          <li
            v-for="num in showPagesFix"
            :key="num"
            class="page-item"
            :class="{ active: numFix(num) == currentPageEdited }"
          >
            <template v-if="numFix(num) == currentPageEdited">
              <span class="page-link">{{ numFix(num) }}</span>
            </template>
            <a
              v-else
              class="page-link"
              href="#"
              @click.prevent="setPage(numFix(num))"
              >{{ numFix(num) }}</a
            >
          </li>
          <li
            class="page-item"
            :class="{ disabled: currentPageEdited == totalPages }"
          >
            <a
              class="page-link"
              href="#"
              @click.prevent="setPage(currentPageEdited + 1)"
              >&gt;</a
            >
          </li>
          <li
            class="page-item"
            :class="{ disabled: currentPageEdited == totalPages }"
          >
            <a class="page-link" href="#" @click.prevent="setPage(totalPages)"
              >&gt;&gt;</a
            >
          </li>
        </ul>
      </nav>
    </div>
  </div>
</template>

<script>
import { computed, ref, watch } from "@vue/runtime-core";
export default {
  name: "BasePagination",
  props: {
    showPages: {
      default: 1,
      require: false,
      type: Number,
    }, //ページネーションを何件表示するか
    currentPage: {
      default: 1,
      require: false,
      type: Number,
    }, //現在のページ
    totalCount: {
      default: 1,
      require: false,
      type: Number,
    }, //総件数
    totalPages: {
      default: 1,
      require: false,
      type: Number,
    }, //総ページ数
    perPage: {
      default: 20,
      require: false,
      type: Number,
    }, //1ページあたりの表示件数
  },
  emits: ["currentPage"],
  setup(props, { emit }) {
    const currentPageEdited = ref(props.currentPage);

    //ページ番号を計算する
    const numFix = (num) => {
      const ajust = 1 + (props.showPages - 1) / 2;
      let result = num;
      //前ページがマイナスになる場合は1からはじめる
      if (currentPageEdited.value > props.showPages / 2) {
        result = num + currentPageEdited.value - ajust;
      }
      //後ページが最大ページを超える場合は最大ページを超えないようにする
      if (currentPageEdited.value + props.showPages / 2 > props.totalPages) {
        result = props.totalPages - props.showPages + num;
      } //総ページ数が表示ページ数に満たない場合、連番そのまま
      if (props.totalPages <= props.showPages) {
        result = num;
      }
      return result;
    };

    //総記事数が表示ページ数以下の場合に調整する
    const showPagesFix = computed(() => {
      if (props.totalPages < props.showPages) {
        return props.totalPages;
      } else {
        return props.showPages;
      }
    });

    //ページネーションを複数設置したときの対応
    watch(
      () => props.currentPage,
      (newValule) => {
        currentPageEdited.value = newValule;
      }
    );

    //何ページ目を表示するか
    const setPage = (page) => {
      //マイナスにならないようにする
      if (page <= 0) {
        currentPageEdited.value = 1;
      }
      //最大ページを超えないようにする
      else if (page > props.totalPages) {
        currentPageEdited.value = props.totalPages;
      } else {
        currentPageEdited.value = page;
      }
      //親コンポーネントに現在のページを送る
      emit("currentPage", currentPageEdited.value);
    };

    return { currentPageEdited, numFix, setPage, showPagesFix };
  },
};
</script>

おわりに

なんとなく動きが怪しいところが見られました (4.5 ページなどが出る) 。これは移植前からなのか、移植したからなのかはわかりません。

これを直したり、自身が扱いやすいようにリファクタリングできたら、と思います。

以上です。

コメントを残す