不求谌解

不求谌解

💻 Web Dev / Creative 💗 ⚽ 🎧 🏓
twitter
github
jike
email

Vueのページネーションコンポーネントを実装する

要件分析#

  • 表示するページ番号ボタンの数をカスタマイズ
  • ページネーションコンポーネントのカラーテーマをカスタマイズ
  • テキストコンテンツまたはアイコンを選択してページを切り替えることができる

実装#

  1. ページネーションコンポーネントが表示するページ番号ボタンの数は、pagesToDisplay()という計算プロパティで定義できます
  2. カラーテーマは、paginationClassのクラスをバインドすることで、色のtypeを返すことで実現できます
  3. テキストまたはアイコンの選択は、ネストされた条件判断テンプレートで実現できます

ページネーションの原理#

ページネーションの実現は、主にこの 2 つのパラメータ、total(総項目数)、perPage(1 ページあたりの表示項目数)に依存します。バックエンドはこれらのパラメータを使用して、フロントエンドに対応するデータを返すことができます。
全体のページネーションコンポーネントの中で、比較的面倒な部分はページ番号リストの表示ロジックです。ページ番号リストは、[最小ページ番号,最大ページ番号] を返す配列です。したがって、問題はページ番号リスト配列のページ番号値の最大値と最小値を求めることに分割されます。ここでは、ページ番号配列の最大値と最小値を返すために、2 つの計算プロパティ maxPage ()、minPage () を定義します。

ページコンポーネント#

<template>
  <ul class="pagination" :class="paginationClass">
    <!-- 前のページ -->
    <li
      class="page-item prev-page"
      :class="{ disabled: value === 1,'no-arrows': noArrows }"
    >
      <a class="page-link" arial-label="Previous" @click="prevPage">
      <!-- テキストがあるか -->
        <template v-if="withText">

        </template>
      <!-- 矢印アイコン -->
        <i class="arrow-left" v-else></i>
      </a>
    </li>
    <!-- 現在表示されているページネーションリスト -->
    <li
      class="page-item"
      v-for="item in range(minPage, maxPage)"
      :key="item"
      :class="{active: value === item}"
    >
      <a class="page-link" @click="changePage(item)">{{item}}</a>
    </li>
    <!-- 次のページ -->
    <li
      class="paga-item next-page"
      :class="{disabled: value === totalPage, 'no-arrows': noArrows}" 
    >
      <a class="page-link" arial-label="Next" @click="nextPage">
      <!-- テキストコンテンツ -->
        <template v-if="withText">

        </template>
        <i class="arrow-right" v-else></i>
      </a>
    </li>
  </ul>
</template>

props#

props: {
  // カラーテーマ
  type: {
    type: String,
    default: "primary",
    // `type`の値を検証
    validator: value => {
      return [
        "default",
        "primary",
        "info",
        "success",
        "danger"
      ].includes(value);
    }
  },
  // テキストコンテンツを持つか
  withText: Boolean,
  // 矢印アイコン
  noArrows: Boolean,
  // ページ番号
  pageCount: {
    type: Number,
    default: 0
  },
  // 1ページあたりの表示項目数
  perPage: {
    type: Number,
    default: 0
  },
  // 総項目数
  total: {
    type: Number,
    default: 0
  },
  // 現在のページ番号の値
  value: {
    type: Number,
    default: 1
  }
}

data#

data: {
  // ページ番号リストに表示されるページ番号の数
  defaultPagesToDisplay: 5
}

computed#

computed: {
  // カラーテーマ
  paginationClass() {
    return `pagination-${this.type}`
  },
  // 総ページ数
  totalPages() {
    if (this.pageCount > 0) return this.pageCount;
    if (this.total > 0 ) {
      return Math.ceil(this.total / this.perPage);
    }
  },
  // 表示されるページ番号ボタンの数
  pagesToDisplay() {
    if (this.totalPages > 0 && this.totalPages < this.defaultPagesToDisplay) {
      return this.totalPages;
    } else {
      return this.defaultPagesToDisplay;
    }
  },
  // ページ番号リストの最小ページ番号
  minPage() {
    if (this.value >= this.pagesToDisplay) {
      // オフセットを定義
      const pagesToAdd = Math.floor(this.pagesToDisplay / 2);
      // 仮定の最大ページ番号 = 現在のページ番号 + オフセット
      const newMaxPage = this.value + pagesToAdd;
      if (newMaxPage > this.totalPages) {
        return totalPages - this.pagesToDisplay + 1;
      } else {
        // 現在のページ番号がページ番号リストの中央に位置する場合
        return this.value - pagesToAdd;
      }
    } else {
      // 現在のページ番号がデフォルトのページ番号リストボタンの数より小さい場合、最小ページ番号は1を返す
      return 1;
    }
  },
  // ページ番号リストの最大ページ番号
  maxPage() {
    if (this.value >= this.pagesToDisplay) {
      const pagesToAdd = Math.floor(this.pagesToDisplay / 2);
      const newMaxPage = this.value + pagesToAdd;
      if (newMaxPage < this.totalPages) {
        return newMaxPage;
      } else {
        return totalPages;
      }
    } else {
      // 現在のページ番号がデフォルトのページ番号リストボタンの数より小さい場合、最大ページ番号は表示されるページ番号リストボタンの数を返す
      return this.pageToDisplay;
    }
  }
}

methods#

methods: {
  // ページ番号リスト配列
  range(min, max) {
    let arr =[];
    for (let i = min; i <= max; i++) {
      arr.push(i);
    }
    return arr;
  },
  // ページ番号を変更
  changePage(item) {
    this.$emit("input", item);
  },
  // 次のページ
  nextPage() {
    if (this.value < this.totalPages) {
      this.$emit("input", this.value + 1);
    }
  },
  // 前のページ
  prevPage() {
    if (this.value > 1) {
      this.$emit("input", this.value - 1);
    }
  }
}

最後に#

自分の技術レベルを向上させたいのであれば、ビジネスコンポーネントを書くことだけでは満足してはいけません。ページネーションのような独立したコンポーネントを書く過程で、APIの設計機能の複雑さについても考えることができました。どんなに複雑なコンポーネントでも、基本的には prop、event、slot の 3 つの部分で構成されています。具体的な実装は、主に基本的な JavaScript の能力に依存しています。
以前、element を模倣して radio と input コンポーネントも作成しました。これらの独立したコンポーネントを書くことを続けて、より大きな世界を発見できることを願っています。この記事のページネーションコンポーネントのソースコードはここにあります。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。