要件分析#
- 表示するページ番号ボタンの数をカスタマイズ
- ページネーションコンポーネントのカラーテーマをカスタマイズ
- テキストコンテンツまたはアイコンを選択してページを切り替えることができる
実装#
- ページネーションコンポーネントが表示するページ番号ボタンの数は、
pagesToDisplay()
という計算プロパティで定義できます - カラーテーマは、
paginationClass
のクラスをバインドすることで、色のtype
を返すことで実現できます - テキストまたはアイコンの選択は、ネストされた条件判断テンプレートで実現できます
ページネーションの原理#
ページネーションの実現は、主にこの 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 コンポーネントも作成しました。これらの独立したコンポーネントを書くことを続けて、より大きな世界を発見できることを願っています。この記事のページネーションコンポーネントのソースコードはここにあります。