🐱

【Newt / Next.js】カテゴリに属するタグ一覧を取得する方法

2024/05/16に公開

やりたいこと

https://devtools-liard.vercel.app/

ページのサイドバーに「カテゴリ」>「タグ」のアコーディオンメニューを作りたい。

前提

基本設定

https://www.newt.so/docs/tutorials/get-contents-in-nextjs
基本はチュートリアル通りにセットアップ済み

カテゴリのフィールド

UID: category

フィールド名 フィールド ID
名前 name
スラッグ catSlug
types/category.ts
import type { Content } from 'newt-client-js'

export interface Category extends Content {
  name: string
  catSlug: string
}
タグのフィールド

UID: tag

フィールド名 フィールド ID
名前 name
スラッグ tagSlug
カテゴリー category
types/tag.ts
import type { Content } from 'newt-client-js'

import type { Category } from '@/types/category'

export interface Tag extends Content {
  name: string
  tagSlug: string
  category: Category
}
アコーディオンメニューの UI

shadcn/uiで作成

実装方法

1. カテゴリ・タグ一覧の取得メソッドを作成する

- カテゴリ一覧の取得メソッド

lib/newt.ts
export const getCategories = cache(async (): Promise<Category[]> => {
  const { items: categories } = await client.getContents<Category>({
    appUid: process.env.NEWT_APP_UID,
    modelUid: 'category',
    query: {
      order: ['_sys.customOrder'],
    },
  })

  return categories
})

- タグ一覧の取得メソッド

lib/newt.ts
export const getTags = async (query?: GetContentsQuery,): Promise<Tag[]> => {
  const { items: tags } = await client.getContents<Tag>({
    appUid: process.env.NEWT_APP_UID,
    modelUid: 'tag',
    query: {
      order: ['_sys.customOrder'],
      ...query,
    },
  })

  return tags
}

getTagsclient.getContentsqueryの引数を渡すことで、任意の条件でタグ一覧を取得できます。

2. カテゴリ一覧を表示する

components/Sidebar.tsx
import { getCategories } from '@/lib/newt'
import {
  Accordion,
  AccordionContent,
  AccordionItem,
  AccordionTrigger,
} from "@/components/ui/accordion"

export default async function Sidebar() {
  // タグ一覧の情報を取得
  const categories = await getCategories()
  return (
    <Accordion type="multiple" className="mat-2">
      {
        // タグ一覧を表示
        categories.map((category) => {
          return (
            <AccordionItem key={category._id} value={category._id}>
              <AccordionTrigger>
                <Link href={`/${category.catSlug}`}>{category.name}</Link>
              </AccordionTrigger>
              <AccordionContent>
                ~~ タグ一覧を表示する ~~
              </AccordionContent>
            </AccordionItem>
          )
        })
      }
    </Accordion>
  )
}

3. タグ一覧を表示する

components/Sidebar.tsx
import { getCategories } from '@/lib/newt'
import {
  Accordion,
  AccordionContent,
  AccordionItem,
  AccordionTrigger,
} from "@/components/ui/accordion"

export default async function Sidebar() {
  const categories = await getCategories()

  const categoriesWithTags = await Promise.all(categories.map(async (category) => {
    const tags = await getTags({ category: category._id })
    return { ...category, tags }
  }))

  return (
    <Accordion type="multiple" className="mat-2">
      {
        categoriesWithTags.map((category) => {
          const tags = category.tags;
          return (
            <AccordionItem key={category._id} value={category._id}>
              <AccordionTrigger>
                <Link href={`/${category.catSlug}`}>{category.name}</Link>
              </AccordionTrigger>
              <AccordionContent>
                <ul className="flex flex-col items-start">
                  {
                    tags.map((tag) => {
                      return (
                        <Button key={tag._id} asChild variant="ghost" className="text-left w-full flex items-start justify-start">
                          <Link href={`/${category.catSlug}/${tag.tagSlug}`}>{tag.name}</Link>
                        </Button>
                      )
                    })
                  }
                </ul>
              </AccordionContent>
            </AccordionItem>
          )
        })
      }
    </Accordion>
  )
}

Discussion