page(ページ番号)とlimit(表示件数)でのページネーション設計を考える
こんにちは、AIQ株式会社のフロントエンドエンジニアのまさぴょんです!
今回は、page(ページ番号)とlimit(表示件数)でのページネーション設計について、解説します。
ページ番号で区切るページネーションの設計をする
ページ番号で区切るページネーションの処理フロー・ステップをまとめると、次のとおりです。
ページ番号で区切るページネーションの処理フロー
- FrontEndから、page(ページ番号)とlimit(表示件数)で、BackEndにGET通信をする。
- 例:
https://robotama-api.com?limit=5&page=1
- 例:
- BackEnd(API)で、page(ページ番号)とlimit(表示件数)から、offset(取得位置)を計算する。
- 通常、
offset = (page - 1) ✖ limit
の式で計算されます。 - たとえば、limit=5 と page=2 の場合、
offset = (2-1) ✖️ 5 = 5
となり、5番目のアイテムから表示を開始します。
- 通常、
- itemList(該当アイテム)とallCount(アイテム総数)を取得するSQLクエリを作成・実行する。
- 例:
SELECT * FROM table LIMIT 5 OFFSET 5;
- 例:
SELECT COUNT(id) FROM テーブル名;
- 例:
- itemList(該当アイテム)とallCount(アイテム総数)をFrontEndに返却する。
- FrontEndに処理が戻って、ページネーション関連のデータの計算をする。
- return(実行結果)で、itemList(該当アイテム), allCount(総アイテム数)を取得する。
- allCount(総アイテム数)と、1ページのlimit(表示件数)が決まっていれば、allPage(総ページの数)は計算できる。
- 例えば、
allCount=36
,limit=5
なら 36 / 5 = 7 あまり 1 で、8 ページ目までページを作成します。
上記のような処理フローで今回は、設計・実装を進めていきます。
登場する要素を整理する
先述の処理フローで、概要は掴めたと思いますが、登場する要素を整理すると、次のとおりです。
登場する要素まとめ
- limit(表示件数)の設定:この値は1ページに表示したいアイテムの数を定義します。
- 例えば、
limit=5
は 1ページに5件のアイテムを表示することを意味します。 - FrontEndで計算します。
- 例えば、
- page(ページ番号):ユーザーがアクセスしているページ番号です。
- 例えば、
page=2
はユーザーが2ページ目を見ていることを示します。 - FrontEndで計算します。
- 例えば、
- offset(取得位置):表示を開始するアイテムのインデックス・取得位置
- 通常、
offset = (page - 1) ✖️ limit
の式で計算されます。 - たとえば、limit=5 と page=2 の場合、
offset = (2-1) ✖️ 5 = 5
となり、5番目のアイテムから表示を開始します。 - BackEndで計算します。
- 通常、
- allCount(アイテムの総数): ページに表示するアイテムの総数です。
- SQLの
COUNT
を使って計算するのが一般的です。 - BackEndで計算します。
- SQLの
- allPage(ページの総数):ページの総数です。
- 例えば、
allCount=36
,limit=5
なら 36 / 5 = 7 あまり 1 で、8 ページ目までページを作成します。 - FrontEndで計算します。
- 例えば、
FrontEndの実装のポイントまとめ
FrontEnd側の処理ポイントをまとめると、次のとおりです。
事例として、FrontEnd(Nuxt・Vueの場合)のComponent内の初期値の状態は、次のような内容です。
data() {
return {
/** ページに表示する MyItem List */
pageItems: [],
/** 選択中の今のページ: 初期値は 1 */
currentPage: 1,
/** 1ページに表示する上限 */
itemLimit: 5,
/** アイテムの総数 */
allCount: 0,
/** すべてのページ数 */
allPage: 0,
};
},
FrontEnd(Nuxt・Vueの場合)のページネーションのFetch処理は、次のような内容です。
/** 表示中のページのアイテムリストを取得する */
async getPageItemList(userId) {
await this.$axios
.get("/api/myItem/pageNation", {
params: {
user_id: userId,
limit: this.itemLimit,
page: this.currentPage,
},
})
.then((res) => {
// 今のページに表示するアイテムをセット
this.pageItems = res.data.itemList;
/** アイテムの総数 */
const allCount = res.data.allCount;
// アイテムの総数をセット
this.allCount = allCount;
// ページ総数をセット (あまりは切り上げる)
this.allPage = Math.ceil(allCount / this.itemLimit);
})
.catch((err) => {
console.error(err);
// this.$router.push('/errorPage')
});
},
総ページ数の計算の際には、あまりは切り上げて計算する点に注意です。
Math.ceil(allCount / this.itemLimit);
BackEndの実装のポイントまとめ
BackEnd側(APIとDB)の処理ポイントをまとめると、次のとおりです。
事例として、API(Express)のController処理を抜粋すると次のような処理です。
/** My Item List を取得する Controller Ver. ページネーション */
exports.getMyItemsPageNation = async (req, res) => {
try {
/** User ID */
const userId = req.query.user_id;
/** 取得するアイテム数(上限値) */
const limit = req.query.limit;
/** ページ番号 */
const page = req.query.page;
/** 取得位置 */
const offset = limit * (page - 1);
/** User の アイテムの総数 */
const allCount = await productManageService.getMyItemsAllCount(userId);
console.log("allCount Result", allCount);
/** 指定ページの My Item List */
const itemList = await productManageService.getMyItemsPageNation(
userId,
limit,
offset
);
res.status(200).send({
data: {
itemList,
allCount,
},
});
} catch (err) {
logger.error(req, err);
res.status(200).send({
error_code: 400,
error: "get myItems error",
});
}
};
まとめ
ページネーションの設計は、今後も使えそうな経験なので、学べてよかったです。
個人で、Blogもやっています、よかったら見てみてください。
注意事項
この記事は、AIQ 株式会社の社員による個人の見解であり、所属する組織の公式見解ではありません。
求む、冒険者!
AIQ株式会社では、一緒に働いてくれるエンジニアを絶賛、募集しております🐱🐹✨
詳しくは、Wantedly (https://www.wantedly.com/companies/aiqlab)を見てみてください。
参考・引用
AIQ 株式会社 に所属するエンジニアが技術情報をお届けします。 ※ AIQ 株式会社 社員による個人の見解であり、所属する組織の公式見解ではありません。 Wantedly: wantedly.com/companies/aiqlab
Discussion