前回(https://www.northdetail.co.jp/blog/3459/)はHeadless CMS「Newt」を使い、蒐集品を管理するサイトを作ってみました。
今回はNuxtとNewtを用いて記事一覧画面をつくり、そこにページャを付けたいと思います。早速やってみましょう。
pages内の階層は次のようになります。
pages/gallery/index.vue 記事一覧トップ
pages/gallery/page/[page].vue 記事一覧の各ページ
今回はnewtを使用するので、あらかじめpluginsで設定を行ってください。
そしてAPIを叩く部分は次のように作ります。
import type { Landscape } from "~/types/landscape"
export const useLandscapeApi = async(
pagekey: string,
limit= 1000,
skip= 0,
param= {}
) => {
try{
const {data} = await useAsyncData(pagekey, async() => {
const {$newtClient} = useNuxtApp()
return await $newtClient.getContents<Landscape>({
appUid: 'landscape',
modelUid: 'post',
query: {
select: ['_id', 'title', 'slug', 'landscape', 'detail'],
limit: limit,
skip: skip,
...param
}
})
})
return data.value
} catch (err: any) {
console.log(err)
return false
}
}
このように、引数として
limit?: number,(一度に取得する件数)
skip?: number,(スキップする件数)
を取ります。
まずは「pages/gallery/index.vue(記事一覧トップ)」と「pages/gallery/page/[page].vue(記事一覧の各ページ)」を作ります。
<template>
<LandscapeList />
</template>
<script lang="ts" setup>
definePageMeta({
title: '綺麗な景色一覧'
})
</script>
<template>
<LandscapeList :key="`landscape-${route.params.page}`"/>
</template>
<script lang="ts" setup>
definePageMeta({
title: '綺麗な景色一覧'
})
</script>
次にここから呼び出している「LandscapeList」コンポーネントを作りましょう。今回は一度に3件の記事を取得することにします。
<template>
<main class="Container">
<article class="landscape" key="article-landscape">
<h2>綺麗な景色一覧</h2>
<ul v-if="landscapeList && landscapeList.length > 0">
<li v-for="landscape in landscapeList"
:key="landscape._id">
<h3>{{ landscape.title }}</h3>
<NuxtLink :to="`/gallery/${landscape.slug}`">
<img :src="landscape.img.src" alt="" :style="{'view-transition-name': `landscape-img-${landscape.slug}`}">
</NuxtLink>
</li>
</ul>
<Pagination :skip="skip" :limit="limit" :total="total" :currentPageNo="currentPageNo" :type="'/gallery'" />
</article>
</main>
</template>
<script lang="ts" setup>
import type { Landscape } from '~/types/landscape'
const route = useRoute()
const currentPageNo = route.params?.page ? Number(route.params.page) : 1
const limit = 3 // 何件ずつ表示するか
const skip = (currentPageNo - 1) * limit
const total = ref(0)
const resData = await useLandscapeApi(
`landscape-${currentPageNo}`,
limit,
(currentPageNo - 1) * limit
)
const landscapeList : Landscape[] | false | null = resData ? resData.items : null
if (resData) {
total.value = resData?.total ?? 0
}
</script>
const currentPageNo = route.params?.page ? Number(route.params.page) : 1
で、現在のページNoを取得します。記事一覧の各ページを見ている場合は[page]の値を取得し、記事一覧トップを見ている場合は「1」とします。
今回は3件ずつ表示するのでlimitは「3」です。
現在のページNoに応じてskipの値を変更します。1ページ目に居るならskipは0。2ページ目に居るなら1ページ目の分は飛ばしたいので、「(現在のページNo(2) - 1)×limit(3)」となります。
また、APIを叩いたときにtotalが取得できるので、refで受け皿を準備しておきます。
その後、limitとskipを引数で渡してAPIを叩きます。
次に「Pagination」コンポーネントを作ります。
<template>
<nav>
<ul>
<li v-for="page in pagerMaxNum" :key="`${routePath}-page-{page}`">
<nuxtLink
:to="page === 1 ? `${type}` : `${type}/page/${page}`"
type="nuxtLink"
:class="`pagination_button ${page === currentPageNo ? '_current' : ''}`">{{ page }}</nuxtLink
>
</li>
</ul>
<p>
{{ props.total }}件中 {{ currentNumberStart }}件目から{{ currentNumberEnd }}件目を表示中
</p>
</nav>
</template>
<script lang="ts" setup>
interface Props {
skip: number
limit: number
total: number,
currentPageNo: number,
type: string
}
const props = withDefaults(defineProps<Props>(), {
skip: 0,
limit: 0,
total: 0,
currentPageNo: 1,
type: ''
})
const route = useRoute()
const routePath = route.path
const currentNumberStart = computed(() => { return props.skip + 1 })
const currentNumberEnd = computed(() => { return (props.skip + props.limit + 1) <= props.total ? (props.skip + props.limit) : props.total })
const pagerMaxNum = computed(() => {
return Math.ceil(props.total / props.limit)
})
</script>
「LandscapeList」からskip、limit、total、currentPageNo、typeが渡ってきています。typeは現在のカテゴリのパスを表しています。今回のサイトにあるのは「綺麗な景色一覧」のみですが、他にも異なるカテゴリ一覧がある場合は、ここの値を変えて対応します。
pagerMaxNumをv-forで回してページャを作ります。1ページ目のときは「pages/gallery/index.vue(記事一覧トップ)」、2ページ目以降のときは「pages/gallery/page/[page].vue(記事一覧の各ページ)」へリンクします。
また、currentNumberStartとcurrentNumberEndで現在表示されているのが何件目から何件目なのかを表示します。totalは「LandscapeList」コンポーネントでAPIを叩いたときに取得できているのでそのまま出力します。
npm run generate→npm run previewで動作確認を行います。
無事、各ページが生成され、ページャから遷移できることが確認できるかと思います。
ページャはブログやコーポレートサイト、その他様々なサイトで必要になってくる機能です。必要になった際はぜひ今回の方法を試してみてください。