microCMS+Astroで記事取得からページネーション実装まで
microCMS+Astroを使う機会があったのでメモ。スタイルまわりにはTailwindCSSを導入しています。
SDKを使ってmicroCMSの情報を取得する
microCMSにはSDKがあるので、それを使って楽に情報を取得できます。
microcmsio/microcms-js-sdk: microCMS JavaScript SDK.
SDK|microCMS|APIベースの日本製ヘッドレスCMS
src/library/microcms.ts
import type { MicroCMSQueries } from "microcms-js-sdk";
import { createClient } from "microcms-js-sdk";
// types
import type { itemType } from '@/types/api'
//ドメインとAPIキーを入れる
const client = createClient({
serviceDomain: import.meta.env.MICROCMS_URL,
apiKey: import.meta.env.MICROCMS_KEY,
});
/**
* 個別記事取得
*/
export const getItem = async (
contentId: string,
queries?: MicroCMSQueries
) => {
return await client.getListDetail<itemType>({
endpoint: "item",
contentId,
queries,
});
};
src/types/api/index.ts
//item
export type itemType = {
id: string,
name: string,
category: {
id: string,
name: string,
slug: string,
}[],
contents:string,
}
ドメインについて
//ドメインとAPIキーを入れる
const client = createClient({
serviceDomain: import.meta.env.MICROCMS_URL,
apiKey: import.meta.env.MICROCMS_KEY,
});
serviceDomain
には、画像の部分を入れます。
全件取得するには
microCMS ヘルプ | 101件以上のコンテンツを取得するにはどうしたらよいですか?
公式のヘルプによると、 getAllContents
() で全件取得できる。
/**
* 全件記事取得
*/
export const getAllItemList = async (
queries?: MicroCMSQueries
) => {
return await client.getAllContents<itemType[]>({ endpoint: "item", queries });
}
getAllContentsの戻り値
//
const response = await getAllItemList({
fields: ["id", "name", "category"],
});
//全件分のデータが配列で戻ってくる
[
{
id: '123456',//microCMSの記事ID
name: 'テストデータ',
category: [
{
id: '123456',
name: 'ダミーカテゴリー',
slug: 'dummy',
},
{
id: '123456',
name: 'ダミーカテゴリー2',
slug: 'dummy2',
}
]
},
{
id: '7890',//microCMSの記事ID
name: 'テストデータ2',
category: [
{
id: '123456',
name: 'ダミーカテゴリー',
slug: 'dummy',
}
]
},
...
]
記事一覧の実装
src/pages/item/[page].astro
---
import Layout from "@/layouts/Layout.astro";
import { getAllItemList } from "@/library/microcms";
import type { itemListSingleType } from "@/types/api";
import ArchiveListSingle from "@/components/atoms/ArchiveListSingle.astro";
import PageNavi from "@/components/atoms/navi/PageNavi.astro";
const PER_PAGE = import.meta.env.ITEM_PER_PAGE; //1ページにつき、いくつ記事を入れるか
export async function getStaticPaths({ paginate }: any) {
const response = await getAllItemList({
fields: ["id", "name", "category"],
});
const list = response;
//paginate()関数で返す
return paginate(list, { pageSize: Number(PER_PAGE) });
}
//受け取る型
interface Props {
page: {
data: itemListSingleType[];
start: number;
end: number;
size: number;
total: number;
currentPage: number;
lastPage: number;
url: {
current: string;
next: string;
prev: string;
};
};
}
const { page } = Astro.props;
---
<Layout title="一覧ページ" description={`いま${page.currentPage}ページ目`}>
<main>
<ul class="grid gap-4 grid-cols-3">
{page.data.map((content) => <ArchiveListSingle {...content} />)}
</ul>
{page && <PageNavi url="/item" itemList={page} />}
</main>
</Layout>
src/types/api/index.ts
//itemListSingle
export type itemListSingleType = {
id: string,
name: string,
category: {
id: string,
name: string,
slug: string,
}[],
}
src/components/atoms/ArchiveListSingle.astro (記事リスト表示用のコンポーネント)
---
const { id, name, category } = Astro.props;
---
<li
class="list-none rounded-xl border-solid bg-[#fbfbfb] shadow-md text-[#333]"
>
<a href={`/item/${id}`} class="p-4 block">
<p class="text-lg font-bold mb-2">{name}</p>
<CategoryList
list={category}
className="flex flex-wrap gap-2"
/>
</a>
</li>
src/components/atoms/list/CategoryList.astro (記事リストの中のカテゴリー表示用のコンポーネント)
---
interface Props {
list:{
id: string;
name: string;
slug: string;
}[],
className:string,
}
const {
list,
className,
link,
} = Astro.props;
---
{
0 < list.length && (
<div class={className}>
{
list.map((itemCategory) => {
return (
<span class={`bg-primary text-white px-2 py-1 text-[12px] rounded-lg ${itemCategory.slug}`} >
{itemCategory.name}
</span>
)
})
}
</div>
)
}
2ページ目以降のルーティングを作成
Astroにはpaginate()
関数があり、それをを使用して2ページ目以降のページを生成します。
src/pages/item/[page].astro
---
import { getAllItemList } from "@/library/microcms";
import type { itemListSingleType } from "@/types/api";
const PER_PAGE = import.meta.env.ITEM_PER_PAGE; //1ページにつき、いくつ記事を入れるか
export async function getStaticPaths({ paginate }: any) {
const response = await getAllItemList({
fields: ["id", "name", "category"],
});
const list = response;
//paginate()関数を使う
return paginate(list, { pageSize: Number(PER_PAGE) });
}
//受け取る型
interface Props {
page: {
data: itemListSingleType[];
start: number;
end: number;
size: number;
total: number;
currentPage: number;
lastPage: number;
url: {
current: string;
next: string;
prev: string;
};
};
}
const { page } = Astro.props;
---
<main>
<p>{`全部で${page.lastPage}ページ`}</p>
<p>{`いま${page.currentPage}ページ目です`}</p>
{page.data.map((content) => {
return (
<>
<p>記事名:{content.name}</p>
</>
)
})}
</main>
参考サイト
ページネーションの実装
src/components/atoms/navi/PageNavi.astro
---
import type { itemListSingleType } from "@/types/api";
interface Props {
url: string;
itemList: {
data: itemListSingleType[];
start: number;
end: number;
size: number;
total: number;
currentPage: number;
lastPage: number;
url: {
current: string;
next: string;
prev: string;
};
};
}
const { url, itemList } = Astro.props;
const STEP = 2; //現在のページの前後表示数
const currentPage = Number(itemList.currentPage); //現在のページ数
let maxPage = itemList.lastPage; //全ページ数
let firstFlg = false; //・・・と最初のリンクを表示するかどうか
let lastFlg = false; //・・・と最後のリンクを表示するかどうか
let firstStep = currentPage - STEP; // 現在のページの前に表示する最初のページ
let lastStep = currentPage + STEP; //現在のページの後に表示する最後のページ
if (firstStep <= 0) {
firstStep = 1;
firstFlg = false;
} else {
if (1 < firstStep) {
firstFlg = true;
} else {
firstFlg = false;
}
}
if (maxPage <= lastStep) {
lastStep = maxPage;
lastFlg = false;
} else {
lastFlg = true;
}
/**
* 配列を出力
* @param start
* @param end
*/
const range = (start: number, end: number) =>
[...Array(end - start + 1)].map((_, i) => start + i);
---
<>
<div class="flex justify-center">
<ul class="flex gap-4 mt-1 c-pageNavi text-[#333]">
{
1 < currentPage && (
<li class="flex bg-white rounded-md shadow-md ">
<a
href={`${url}/page/${currentPage - 1}`}
class="flex items-center px-3 py-2"
>
<
</a>
</li>
)
}
{
firstFlg && (
<>
<li class="flex bg-white rounded-md shadow-md">
<a href={`${url}/page/1`} class="flex items-center px-3 py-2">
1
</a>
</li>
<li class="flex text-white items-center">...</li>
</>
)
}
{
range(firstStep, lastStep).map((number) => (
<li
class={`flex bg-white rounded-md shadow-md ${number === currentPage ? "is-current" : ""}`}
>
{number === currentPage ? (
<span class="flex items-center px-3 py-2">{number}</span>
) : (
<a
href={`${url}/page/${number}`}
class="flex items-center px-3 py-2"
>
{number}
</a>
)}
</li>
))
}
{
lastFlg && (
<>
<li class="flex text-white items-center">...</li>
<li class="flex bg-white rounded-md shadow-md">
<a
href={`${url}/page/${maxPage}`}
class="flex items-center px-3 py-2"
>
{maxPage}
</a>
</li>
</>
)
}
{
currentPage < maxPage && (
<li class="flex bg-white rounded-md shadow-md ">
<a
href={`${url}/page/${currentPage + 1}`}
class="flex items-center px-3 py-2"
>
>
</a>
</li>
)
}
</ul>
</div>
</>
src/pages/item/[page].astro (PageNaviコンポーネントを読み込む)
---
import Layout from "@/layouts/Layout.astro";
import { getAllItemList } from "@/library/microcms";
import type { itemListSingleType } from "@/types/api";
import ArchiveListSingle from "@/components/atoms/ArchiveListSingle.astro";
import PageNavi from "@/components/atoms/navi/PageNavi.astro";
const PER_PAGE = import.meta.env.ITEM_PER_PAGE; //1ページにつき、いくつ記事を入れるか
export async function getStaticPaths({ paginate }: any) {
const response = await getAllItemList({
fields: ["id", "name", "category"],
});
const list = response;
//paginate()関数を使う
return paginate(list, { pageSize: Number(PER_PAGE) });
}
//受け取る型
interface Props {
page: {
data: itemListSingleType[];
start: number;
end: number;
size: number;
total: number;
currentPage: number;
lastPage: number;
url: {
current: string;
next: string;
prev: string;
};
};
}
const { page } = Astro.props;
---
<Layout title="一覧ページ" description={`いま${page.currentPage}ページ目`}>
<main>
<ul class="grid gap-4 grid-cols-3">
{page.data.map((content) => <ArchiveListSingle {...content} />)}
</ul>
//propsの情報をコンポーネントへ渡す
{page && <PageNavi url="/item" itemList={page} />}
</main>
</Layout>
参考サイト
microcmsio/microcms-js-sdk: microCMS JavaScript SDK.