<script lang="ts">
export interface PageNumber {
value: number;
}
export interface Ellipsis {
value: string;
}
export type PageItem = PageNumber | Ellipsis;
</script>
<script setup lang="ts">
import { defineProps, PropType } from 'vue';
import { Link } from '@inertiajs/vue3';
const props = defineProps({
totalResults: {
type: Number as PropType<number>,
required: true,
},
currentPage: {
type: Number as PropType<number>,
required: true,
},
totalPage: {
type: Number as PropType<number>,
required: true,
},
});
// Emitイベントを定義
const emit = defineEmits(['update:page']);
// ページを変更する関数
const changePage = (page: number) => {
emit('update:page', page);
};
// 次のページに移動
const nextPage = () => {
if (props.currentPage < props.totalPage) {
changePage(props.currentPage + 1);
}
};
// 前のページに移動
const previousPage = () => {
if (props.currentPage > 1) {
changePage(props.currentPage - 1);
}
};
// ページ番号の配列を取得するメイン関数
const getPages = () => {
const pages: PageItem[] = [];
const maxPagesToShow = 7; //表示するページ数
const sidePages = Math.floor((maxPagesToShow - 3) / 2);
// 全体のページ数が表示するページ数以下の場合は、単純にすべてのページ番号を表示する
if (props.totalPage <= maxPagesToShow) {
for (let i = 1; i <= props.totalPage; i++) {
pages.push({ value: i });
}
return pages;
}
const { startPage, endPage } = getMiddlePages(props.currentPage, props.totalPage, sidePages, maxPagesToShow);
addBoundaryPages(pages, props.totalPage);
if (startPage > 2) {
addEllipsis(pages, 'start');
}
for (let i = startPage; i <= endPage; i++) {
pages.splice(pages.length - 1, 0, { value: i });
}
if (endPage < props.totalPage - 1) {
addEllipsis(pages, 'end');
}
return pages;
};
/**
* getPages()の中で呼ばれる関数群
*/
// 1ページ目と最後のページを追加する関数
const addBoundaryPages = (pages: PageItem[], totalPages: number) => {
pages.push({ value: 1 });
if (totalPages > 1) {
pages.push({ value: totalPages });
}
};
// 「...」を追加する関数
const addEllipsis = (pages: PageItem[], position: 'start' | 'end') => {
const ellipsis: PageItem = { value: '...' };
if (position === 'start') {
pages.splice(1, 0, ellipsis);
} else {
pages.splice(pages.length - 1, 0, ellipsis);
}
};
// 中間ページの範囲を取得する関数
const getMiddlePages = (currentPage: number, totalPages: number, sidePages: number, maxPagesToShow: number) => {
let startPage = Math.max(2, currentPage - sidePages);
let endPage = Math.min(totalPages - 1, currentPage + sidePages);
if (currentPage <= sidePages + 2) {
startPage = 2;
endPage = maxPagesToShow - 1;
} else if (currentPage > totalPages - (sidePages + 2)) {
startPage = totalPages - (maxPagesToShow - 2);
endPage = totalPages - 1;
}
return { startPage, endPage };
};
</script>
<template>
<div class="pagination">
<span>検索結果:{{ totalResults }}件</span>
<!-- 前のページへ移動 -->
<span :class="{ disabled: currentPage === 1 }">
<template v-if="currentPage > 1">
<Link @click="previousPage" href="#"> < </Link>
</template>
<template v-else>
<span><</span>
</template>
</span>
<!-- ページ番号のリンク -->
<span>
<span
v-for="page in getPages()"
:key="page.value"
:class="{ active: currentPage === page.value, ellipsis: page.value === '...' }"
>
<template v-if="page.value === '...'">
<span>{{ page.value }}</span>
</template>
<template v-else-if="currentPage === page.value">
<span>{{ page.value }}</span>
</template>
<template v-else>
<Link @click="changePage(page.value)" href="#">{{ page.value }}</Link>
</template>
</span>
</span>
<!-- 次のページへ移動 -->
<span :class="{ disabled: currentPage === totalPage }">
<template v-if="currentPage < totalPage">
<Link @click="nextPage" href="#"> > </Link>
</template>
<template v-else>
<span>></span>
</template>
</span>
</div>
</template>
Vue.js + Inertia.js でページネーションを実装する方法
このページでは、Vue.js + Inertia.js を使用したページネーションコンポーネント の作成方法を解説します。
このコンポーネントを使うことで、検索結果や一覧ページに適切なページネーションを追加 できます。
📌 このページネーションでできること
✅ 現在のページに応じたページネーションを自動生成!
✅ 前後のページに簡単に移動可能!
✅ ページが多い場合は … を表示し、ページリストを適切に縮小!
✅ 現在のページを強調表示し、クリックでページ遷移が可能!
✅ Inertia.js の emit(‘update:page’, page) を使い、ページ番号を変更可能!
🛠 親コンポーネントでの使い方
ページネーションを表示したいページで、以下のように Pagination コンポーネントを使用します。
<Pagination
:totalResults="150"
:currentPage="3"
:totalPage="10"
@update:page="handlePageChange"
/>
この場合のページネーション
検索結果:150件
[ < ] 1 ... 2 3 4 5 ... 10 [ > ]
• totalResults → 検索結果の総数
• currentPage → 現在のページ番号
• totalPage → 総ページ数
• @update:page=”handlePageChange” → 親コンポーネント側でページ変更時の処理を実装
📌 ページネーションの実装
以下が、Vue.js でページネーションを動的に生成するコンポーネントです。
<script lang="ts">
export interface PageNumber {
value: number;
}
export interface Ellipsis {
value: string;
}
export type PageItem = PageNumber | Ellipsis;
</script>
<script setup lang="ts">
import { defineProps, PropType } from 'vue';
import { Link } from '@inertiajs/vue3';
const props = defineProps({
totalResults: {
type: Number as PropType<number>,
required: true,
},
currentPage: {
type: Number as PropType<number>,
required: true,
},
totalPage: {
type: Number as PropType<number>,
required: true,
},
});
// Emitイベントを定義
const emit = defineEmits(['update:page']);
// ページを変更する関数
const changePage = (page: number) => {
emit('update:page', page);
};
// 次のページに移動
const nextPage = () => {
if (props.currentPage < props.totalPage) {
changePage(props.currentPage + 1);
}
};
// 前のページに移動
const previousPage = () => {
if (props.currentPage > 1) {
changePage(props.currentPage - 1);
}
};
// 📌 ページ番号の配列を取得するメイン関数
const getPages = () => {
const pages: PageItem[] = [];
const maxPagesToShow = 7; // 表示するページ数
const sidePages = Math.floor((maxPagesToShow - 3) / 2);
// 全体のページ数が表示するページ数以下の場合は、単純にすべてのページ番号を表示する
if (props.totalPage <= maxPagesToShow) {
for (let i = 1; i <= props.totalPage; i++) {
pages.push({ value: i });
}
return pages;
}
const { startPage, endPage } = getMiddlePages(props.currentPage, props.totalPage, sidePages, maxPagesToShow);
addBoundaryPages(pages, props.totalPage);
if (startPage > 2) {
addEllipsis(pages, 'start');
}
for (let i = startPage; i <= endPage; i++) {
pages.splice(pages.length - 1, 0, { value: i });
}
if (endPage < props.totalPage - 1) {
addEllipsis(pages, 'end');
}
return pages;
};
/**
* getPages() の中で呼ばれる関数群
*/
// 1ページ目と最後のページを追加する関数
const addBoundaryPages = (pages: PageItem[], totalPages: number) => {
pages.push({ value: 1 });
if (totalPages > 1) {
pages.push({ value: totalPages });
}
};
// 「...」を追加する関数
const addEllipsis = (pages: PageItem[], position: 'start' | 'end') => {
const ellipsis: PageItem = { value: '...' };
if (position === 'start') {
pages.splice(1, 0, ellipsis);
} else {
pages.splice(pages.length - 1, 0, ellipsis);
}
};
// 中間ページの範囲を取得する関数
const getMiddlePages = (currentPage: number, totalPages: number, sidePages: number, maxPagesToShow: number) => {
let startPage = Math.max(2, currentPage - sidePages);
let endPage = Math.min(totalPages - 1, currentPage + sidePages);
if (currentPage <= sidePages + 2) {
startPage = 2;
endPage = maxPagesToShow - 1;
} else if (currentPage > totalPages - (sidePages + 2)) {
startPage = totalPages - (maxPagesToShow - 2);
endPage = totalPages - 1;
}
return { startPage, endPage };
};
</script>
<template>
<div class="pagination">
<span>検索結果:{{ totalResults }}件</span>
<!-- 前のページへ移動 -->
<span :class="{ disabled: currentPage === 1 }">
<template v-if="currentPage > 1">
<Link @click="previousPage" href="#"> < </Link>
</template>
<template v-else>
<span><</span>
</template>
</span>
<!-- ページ番号のリンク -->
<span>
<span
v-for="page in getPages()"
:key="page.value"
:class="{ active: currentPage === page.value, ellipsis: page.value === '...' }"
>
<template v-if="page.value === '...'">
<span>{{ page.value }}</span>
</template>
<template v-else-if="currentPage === page.value">
<span>{{ page.value }}</span>
</template>
<template v-else>
<Link @click="changePage(page.value)" href="#">{{ page.value }}</Link>
</template>
</span>
</span>
<!-- 次のページへ移動 -->
<span :class="{ disabled: currentPage === totalPage }">
<template v-if="currentPage < totalPage">
<Link @click="nextPage" href="#"> > </Link>
</template>
<template v-else>
<span>></span>
</template>
</span>
</div>
</template>
✅ このページネーションのポイント
✔ totalResults を表示し、検索結果の総数をユーザーに示す!
✔ 現在のページはハイライト表示し、クリックできないようにする!
✔ ページ数が多い場合は … を追加して適切に省略!
✔ emit(‘update:page’, page) を使い、親コンポーネントからページ変更が可能!
🚀 まとめ
このコンポーネントを導入することで、Vue + Inertia.js を使用した 動的なページネーション を簡単に追加できます! 🚀
コメント