Nuxt.jsmicroCMS

Nuxt3とmicroCMSで作るブログ ⑥タグでの絞り込み

公開日:2022-04-30 更新日:2023-06-11

Nuxt3とmicroCMSで作るブログシリーズの6番目の記事です。

今回はタグでの絞り込みを実装していきます。

ルートの追加

まずは、タグで絞り込まれた場合のルートを追加します。

階層が深いですが、pagesディレクトリにtags[tagId]page[id].vueと作成します。

<!-- src/pages/tags/[tagId]/page/[id].vue -->
<script setup lang="ts">
const route = useRoute()
const page = Number(route.params.id || 1)
const tagId = String(route.params.tagId || '')
</script>
<template>
    <Home :page="page" :tagId="tagId" />
</template>

こうしておくことで、例えば/tag/my-pretty-tag/page/1にアクセスされた際に、route.params.tagIdとすればmy-pretty-tagというタグIDが取り出せるようになります。

タグ一覧取得API

server/api配下に作成していきます。

// src/server/api/tagList.ts
import client from './client'
import { Tag } from '../../types/blog'

export default defineEventHandler(async (event) => {
    // これ以上は増えない、という値
    const queries = {
        limit: 100,
    }
    const data = await client.getList<Tag>({
        endpoint: 'tag',
        queries: queries
    })
    return data
})

記事一覧と違ってタグは基本的にすべて表示させます。

なので、limitはこれより増えることはないだろう、という値を設定しています。

タグ一覧コンポーネンツ

次にTags.vueコンポーネンツを作成していきます。

<!-- src/components/Tags.vue -->
<script setup lang="ts">
import { Tag } from '../types/blog';

type Props = {
    tags: Tag[];
    selectedTagId: string | undefined;
}

const { tags, selectedTagId } = defineProps<Props>();

function getClass(tagId: string) {
    if (tagId == selectedTagId) return 'active'
    return 'link'
}

</script>

<template>
    <div class="wrapper">
        <h1 class="pageTitle">タグ</h1>
        <ul>
            <li v-for="tag in tags" :key="tag.id" class="list">
                <NuxtLink :to="`/tags/${tag.id}/page/1`" :class="getClass(tag.id)">
                    {{ tag.name }}
                </NuxtLink>
            </li>
        </ul>
    </div>
</template>

<style scoped>
.wrapper {
    padding: 20px 0;
}

.pageTitle {
    font-size: 2rem;
    font-weight: bold;
    color: #5ba9f7;
    background-color: #c8e4ff;
    padding: 6px 10px;
    margin-bottom: 10px;
    border-radius: 5px;
}

.list {
    border-bottom: 1px solid #eee;
    list-style-type: none;
}

.link,
.active {
    display: block;
    padding: 10px;
    color: #888;
    font-size: 1.6rem;
}

.active {
    color: #5ba9f7;
}
</style>

タグ一覧と選択中のタグIDを親から受け取り、リンクを表示させるだけのシンプルな作りです。

選択中のタグとそうでない場合でクラスを変えて、どのタグを選択しているか色で表示するようにしています。

function getClass(tagId: string) {
    if (tagId == selectedTagId) return 'active'
    return 'link'
}

Home.vueの更新

次にHome.vueを更新していきます。

追加でやりたいことは以下の2点です。

  • tagありのルーティングから呼ばれたときは記事一覧を絞り込む
  • tag一覧を取得してTagsに渡す

<!-- src/components.Home.vue -->
<script setup lang="ts">
import { MicroCMSQueries } from 'microcms-js-sdk';
import { BLOG_PER_PAGE } from '../settings/siteSettings';

type Props = {
    page: number,
    // オプショナルで追加
    tagId?: string
}

const { page, tagId } = defineProps<Props>()

const limit = BLOG_PER_PAGE
const queries: MicroCMSQueries = {
    limit: limit,
    offset: (page - 1) * limit,
}

//tagIdを渡されているときはqueriesに加える
if (tagId) {
    queries.filters = `tag[contains]${tagId}`
}

const { data: posts } = await useFetch('/api/postList', { params: queries })
// 追加 タグ一覧の取得
const { data: tags } = await useFetch('/api/tagList')

const numPages = Math.ceil(posts.value.totalCount / limit)

</script>

<template>
    <div>
        <div class="divider">
            <section class="container">
                <PostList :posts="posts.contents" />
            </section>
            <aside class="aside">
                <SearchForm />
                <!-- 追加 -->
                <Tags :tags="tags.contents" :selectedTagId="tagId" />
            </aside>
        </div>
        <Pagination :numPages="numPages" :current="page" />
    </div>
</template>

先ほど作成したタグ一覧コンポーネントを配置しつつ、URLからタグIDを取得してparamに加えるようにしています。

タグIDが存在しないこともあるのでif分でparamに追加するようにしました。

//tagIdを渡されているときはqueriesに加える
if (tagId) {
    queries.filters = `tag[contains]${tagId}`
}

次に記事一覧APIでもタグIDをキャッチできるようにしていきます。

この段階でタグでの絞り込みが行えるようになります。

タグmicroCMSを選択した状態

タグありのページネーション

だいたいできてきましたが、タグありのページネーションも追加する必要があります。

いまのままですと、タグを絞り込んだ状態で2ページ目に遷移した際に、絞り込みが解除されてしまいます。

これは簡単で現在のページネーションのコンポーネントを少し編集するだけです。

<!-- src/components/Pagination.vue -->
<script setup lang="ts">

type Props = {
    numPages: number;
    current: number;
    // タグIDを受け取るようにする
    selectedTagId?: string | undefined;
    keyword?: string;
}

const { numPages, current, selectedTagId, keyword } = defineProps<Props>();

function getPath(p: number) {
    // 追加 タグありのリンク
    if (selectedTagId) return `tag/${selectedTagId}/page/${p}`
    if (keyword) return `/search?q=${keyword}&page=${p}`
    return `/page/${p}`
}

function getClass(page: number, current: number) {
    if (page == current) return 'current'
    return 'link'
}

</script>

あとは親からPaginationに対してタグIDを渡すだけです。

<!-- src/components/Home.vue -->
<template>
    <div>
        <div class="divider">
          ...
        </div>
        <Pagination :numPages="numPages" :current="page" :selectedTagId="selectedTagId" />
    </div>
</template>

おわりに

こちらでこのシリーズを一旦終了とします。


Twitter Share