🧠 概要:
概要
この記事では、WordPressとAstro.jsを使用したヘッドレスCMSの構築プロセスを紹介しています。特に、ページネーション機能をコンポーネント化する方法に焦点を当てています。このコンポーネント化により、他のカテゴリーや記事ページでも再利用が可能になり、管理が簡単になります。
要約(箇条書き)
- 目標:
Pagination.astro
コンポーネントを作成し、どのページでも簡単にページネーションを利用できるようにする。 - ステップ①:共通コンポーネントを新規作成し、
totalPages
、currentPage
、baseUrl
というpropsを受け取る。 - HTML構造:ページネーションの表示ロジックは論理演算子を使って簡潔に書く。
- 補足説明:
- 論理演算子(
&&
)の使用例と三項演算子での条件分岐機能を紹介。
- 論理演算子(
- ステップ②:スタイルの設定を行い、視覚的な整備を施す。
- ステップ③:
- タグページやカテゴリページにコンポーネントを組み込み、実際に表示を確認。
- ビルド後、正しくページネーションが表示されることを確認。
- 最終確認:異なるタイプのページにコンポーネントを適用することで、再利用の流れを見せる。次回はリダイレクトの設定が予定されている。
この構築方法により、開発者は効率的にページネーション機能を実装し、再利用性を高めることができます。
まだまだページネーションの実装が続きます!😅
今回は前回で作ったページネーションを、他のカテゴリーや記事ページでも使えるようにコンポーネント化します!
ページネーションのコンポーネント化のゴール
-
Pagination.astro コンポーネントを作成
-
どのページでも <Pagination totalPages={x} currentPage={y} baseUrl=”/xxx/” /> のように呼び出すだけでOK
-
見た目や構造は1か所で管理
さてやってみましょう!
ステップ①:共通コンポーネントを作成
ファイルを下記に作ります
src/components/Pagination.astro
まずはスクリプトですが、コンポーネントの中で props を受け取るだけなのでシンプルです😁
--- const { totalPages, currentPage, baseUrl } = Astro.props;---
totalPages、currentPage、baseUrl はコンポーネントを読み込む際に、propsを渡します。
htmlは前回追加したページネーションをそのまま持ってきますが、論理演算子(&&)で分岐させます
{totalPages > 1 && ( <nav class="pagination"> <ul> {currentPage > 1 && ( <li> <a href={`${baseUrl}${currentPage - 1}/`}>← Prev</a> </li> )} {Array.from({ length: totalPages }).map((_, i) => { const pageNum = i + 1; return ( <li> {pageNum === currentPage ? ( <span class="current">{pageNum}</span> ) : ( <a href={`${baseUrl}${pageNum}/`}>{pageNum}</a> )} </li> ); })} {currentPage < totalPages && ( <li> <a href={`${baseUrl}${currentPage + 1}/`}>Next →</a> </li> )} </ul> </nav>)}
補足:論理演算子(&&)について
論理演算子(&&)は、左辺が ture の時のみ(1ページしかないならナビゲーションを表示しない)右辺が適用されるようにします
JSX構文では、if 文の代わりによく使うテクニックで、AstroやReactでは
if (totalPages > 1) { return <nav>...</nav>}
という形より
{totalPages > 1 && <nav>...</nav>}
という書き方の方が簡潔でテンプレートに馴染みやすいです。
補足2:もっと複雑な条件を書くなら三項演算子
もっと複雑にすると、下記のように三項演算子で書くことが出来ます。
{totalPages === 0 ? <p>記事がありません</p> : <Pagination totalPages={totalPages} ... />}
ステップ②:スタイル(任意)
.pagination ul { display: flex; gap: 0.5rem; list-style: none; padding: 0;} .pagination li a,.pagination li span { display: inline-block; padding: 0.4em 0.8em; border: 1px solid #ccc; border-radius: 4px; text-decoration: none; color: #333;} .pagination li .current { font-weight: bold; background-color: #eee; pointer-events: none;}
前回ページに記述していたcssを、Pagination.astro に記述します。
Pagination.astro の全体は
--- const { totalPages, currentPage, baseUrl } = Astro.props;--- {totalPages > 1 && ( <nav class="pagination"> <ul> {currentPage > 1 && ( <li> <a href={`${baseUrl}${currentPage - 1}/`}>← Prev</a> </li> )} {Array.from({ length: totalPages }).map((_, i) => { const pageNum = i + 1; return ( <li> {pageNum === currentPage ? ( <span class="current">{pageNum}</span> ) : ( <a href={`${baseUrl}${pageNum}/`}>{pageNum}</a> )} </li> ); })} {currentPage < totalPages && ( <li> <a href={`${baseUrl}${currentPage + 1}/`}>Next →</a> </li> )} </ul> </nav>)} <style>.pagination ul { display: flex; gap: 0.5rem; list-style: none; padding: 0;} .pagination li a,.pagination li span { display: inline-block; padding: 0.4em 0.8em; border: 1px solid border-radius: 4px; text-decoration: none; color: }.pagination li .current { font-weight: bold; background-color: pointer-events: none;}</style>
こんな感じです。
ステップ③:使い方(タグページ・カテゴリページなど)
例としてタグページに記述してみます!
タグページの css は必要ないので、style タグごと削除しておきます。
まずは script で Pagination.astro を import します!
import Pagination from '../../../../components/Pagination.astro';
そして、html に コンポーネントを記述して、コンポーネントのタグにコンポーネントに渡す props を記述しておきます。
<Pagination totalPages={totalPages} currentPage={currentPage} baseUrl={`/tags/${tag.slug}/page/`}/>
ビルドしてみると
ちゃんと出ました😀!
カテゴリーページに組み込んでみる
次はこのコンポーネントをカテゴリーページにも組み込んでみます。
カテゴリーのディレクトリ内にも、タグページ同様に、
/categories/[slug]/page/[page].astro
とファイルを作っておきます。
そしてそのファイルにスクリプトをhtmlを記述します。
---import Pagination from '../../../../components/Pagination.astro'; export async function getStaticPaths() { const res = await fetch('http://headless.local/wp-json/wp/v2/categories'); const categories = await res.json(); const paths = []; for (const category of categories) { const count = category.count; const totalPages = Math.ceil(count / 4); for (let page = 1; page <= totalPages; page++) { paths.push({ params: { slug: category.slug, page: page.toString(), }, props: { category, currentPage: page, }, }); } } return paths;}const { category, currentPage } = Astro.props;const totalPages = Math.ceil(category.count / 4); let posts = [];let fetchError = false; try { const res = await fetch(`http://headless.local/wp-json/wp/v2/posts?categories=${category.id}&per_page=4&page=${currentPage}&_embed`); if (!res.ok) throw new Error(`記事取得失敗: ${res.status}`); posts = await res.json();} catch (err) { console.error("記事取得エラー:", err); fetchError = true;}---<html lang="ja"> <head> <meta charset="UTF-8" /> <title>{category.name} の記事一覧({currentPage}ページ目)</title> </head> <body> <h1>カテゴリー: {category.name}</h1> {fetchError ? ( <p style="color:red;">記事の取得に失敗しました。</p> ) : posts.length === 0 ? ( <p>このカテゴリーには記事がありません。</p> ) : ( <ul> {posts.map(post => ( <li> <a href={`/blog/${post.slug}/`}> <h2>{post.title.rendered}</h2> <p set:html={post.excerpt.rendered}></p> </a> </li> ))} </ul> )} <!-- ✅ ページネーションコンポーネントを表示 --> <Pagination totalPages={totalPages} currentPage={currentPage} baseUrl={`/categories/${category.slug}/page/`} /> <p><a href="/categories/">← カテゴリー一覧に戻る</a></p> </body></html>
/categories/overseas-travel/page/1/
にアクセスしてみると…
こちらにもページネーションが表示されました😆!
次回はリダイレクトの設定をします!
Views: 0