個別のページにタグを持たせたい

当初はHugoとかを用いてお勉強したことをまとめていこうと思っていたけど、今はとりあえずここに残していこうとしている。 で、今の作りだとページが増えてくると欲しい情報を探すのが大変になりそう。 検索機能を持たせられるといいのかもしれないけど、まずは個別のページにタグを設けてひっこ抜けるようにしたい。

やりたいことは

  • 個別ページにタグを持たせる
  • タグ一覧を作る
  • タグ毎にページの一覧を作る

個別のページにタグを設ける

どこに書くか

個別のページに設定するからマークダウンファイルの中に書けばいいと思う。 Hugo だと front matter の中にカテゴリとかタグを書けたはずなので、それを参考にして ftont matter に書くようにする。 元々の front matter は title と date を持たせている。

---
title: '個別のページにタグを持たせたい'
date: '2022-10-29'
---

gray-matter

posts.js の中でインポートしている gray-matter というライブラリがマークダウンファイルの front matter を解析してくれるらしい。 以下の内容の front-metter だったら、

---
title: Hello
slug: home
---
<h1>Hello world!</h1>

以下の内容のオブジェクトに変換してくれる。

{
  content: '<h1>Hello world!</h1>',
  data: { 
    title: 'Hello', 
    slug: 'home' 
  }
}

タグの設け方

ページにタグを持たせるなら複数持たせたいけど、front-matter に指定するならどうしたらいいか。 結論、YAML?のリストみたいな書き方をすればいいらしい。

---
title: '個別のページにタグを持たせたい'
date: '2022-10-29'
tag:
- 'note'
- 'nextjs'
---

[id].js の方にタグを書き出す処理を書いた。 繰り返し処理の書き方が不思議。

<p class="post_tag">
  {postData.tags.map((val) =>
    <span class="post_tag_item">{val}</span> 
  )}
</p>

タグ一覧のページを追加

はじめに lib/posts.js にタグ一覧を取得する関数を追加。

export function getAllTags() {
  const allPosts = getSortedPostsData();

  let tags = [];
  allPosts.map((post) => {
    tags = [...tags, ...post.tags];
  });

  let sortedTags = [...new Set(tags)];

  return sortedTags.sort();
}

タグの一覧を表示するページとして pages/tag.js を追加。

export default function Tag({ allTags }) {
  return (
    <Layout home>
      <Head>
        <title>タグ一覧</title>
      </Head>

      <section className={`${utilStyles.headingMd} ${utilStyles.padding1px}`}>
        <h2>タグ一覧</h2>

        <ul class="tags">
          {allTags.map((tag) => (
            <li class="tag_item" key={tag}>
              <Link href={`/tag/${encodeURIComponent(tag)}`}>
                <a>{tag}</a>
              </Link>
            </li>
          ))}
        </ul>
      </section>
    </Layout>
  )
}

export async function getStaticProps() {
  // Add the "await" keyword like this:
  const allTags = getAllTags();
  return {
    props: {
      allTags,
    },
  };
}

これでタグの一覧が表示されるようなったが、front matter にタグが設定されていないマークダウンファイルがあるとエラーで落ちてしまうのは注意。

タグ毎のページを作成

タグ毎のページのURLは /tag/タグ名 となるようにしたい。 考え方は個別のページと似ていると思うので、pages/tag/[tag].js を作る。

export default function Tag({ postData, tag }) {
  return (
    <Layout home>
      <Head>
        <title>Tag: {tag}</title>
      </Head>

      <section className={`${utilStyles.headingMd} ${utilStyles.padding1px}`}>
        <h2>Tag: {tag}</h2>

        <ul class="posts">
          {postData.map(({ id, date, title }) => (
            <li class="post_item" key={id}>
              <Link href={`/posts/${id}`}>
                <a>{title}</a>
              </Link>
              <br />
              <small class="post_date">
                <Date dateString={date} />
              </small>
            </li>
          ))}
        </ul>
      </section>
    </Layout>
  )
}

export async function getStaticPaths() {
  const tags = getAllTags();
  const paths = tags.map((tag) => {
    return {
      params: {
        tag: tag,
      },
    };
  });

  return {
    paths,
    fallback: false,
  };
}

export async function getStaticProps({ params }) {
  const tag = params.tag;
  const allPosts = getSortedPostsData();
  const postData = allPosts.filter( (data) => data.tags.includes(tag) );

  return {
    props: {
      postData,
      tag,
    },
  };
}

個別ページのタグにリンクを

タグにリンクを持たせたら完成。

<p class="post_tag">
  {postData.tags.map((val) =>
    <span class="post_tag_item">
      <Link href={`/tag/${encodeURIComponent(val)}`}>
        <a>{val}</a>
      </Link>
    </span>
  )}
</p>

感想

  • javascriptが勉強不足でした。
  • Next.jsは難しかった。
  • あとでもう少しわかりやすくまとめたい。
kawaii.

MHz

気が向いたときに何かをメモする。