💻

gatsby-blogにtag機能を追加した
May 26, 2022February 20, 2024
#Develop#ブログ制作#gatsby

概要

開設当初からブログのタグ付をしたかったが、実装に時間がかかりそうだったので諦めていた。
今回少し時間ができたので実装してみた。

実装の流れ

  • tag 一覧表示ページのを作成する。
  • 各 tag の記事一覧ページを作成する。
  • 記事の YAML Front Matter に記載した tag 情報を読めるようにする。
  • 各記事に該当する tag を表示する。

tag 一覧表示ページのを作成する

あまり利用を想定していないので公式から変更なし。
新しい tag をつけるときの参考や重複防止が主な役割なのでスタイルすら当てていない。
各 tag の記事数が出るところが管理上地味に便利。

各 tag の記事一覧ページを作成する

tag ごとの記事一覧ページは Home の記事一覧ページをコピーして流用した。

import React from "react"
import PropTypes from "prop-types"
import { Link, graphql } from "gatsby"
import { CssBaseline, Grid, Typography } from "@mui/material"
import Layout from "../components/layout"
import SEO from "../components/seo"
import Postcard from "../components/postscard"

const Tags = ({ pageContext, data, location }) => {
  const siteTitle = data.site.siteMetadata?.title || `Title`
  const { tag } = pageContext
  const { edges, totalCount } = data.allMarkdownRemark
  const tagHeader = `${tag}:(${totalCount})`
  return (
    <React.Fragment>
      <CssBaseline />
      <Layout location={location} title={siteTitle}>
        <SEO title={`#` + tag} />
        <header>
          <Typography variant="h5" component="div" itemProp="headline">
            {`#` + tagHeader}
          </Typography>
        </header>
        <hr />
        <ul>
          {edges.map(({ node }) => {
            return <Postcard post={node} />
          })}
        </ul>
        {/*
              This links to a page that does not yet exist.
              You'll come back to it!
            */}
        <Link to="/tags">All tags</Link>
      </Layout>
    </React.Fragment>
  )
}
Tags.propTypes = {
  pageContext: PropTypes.shape({
    tag: PropTypes.string.isRequired,
  }),
  data: PropTypes.shape({
    allMarkdownRemark: PropTypes.shape({
      totalCount: PropTypes.number.isRequired,
      edges: PropTypes.arrayOf(
        PropTypes.shape({
          node: PropTypes.shape({
            frontmatter: PropTypes.shape({
              title: PropTypes.string.isRequired,
            }),
            fields: PropTypes.shape({
              slug: PropTypes.string.isRequired,
            }),
          }),
        }).isRequired
      ),
    }),
  }),
}
export default Tags
export const pageQuery = graphql`
  query ($tag: String) {
    allMarkdownRemark(
      limit: 2000
      sort: { fields: [frontmatter___date], order: DESC }
      filter: { frontmatter: { tags: { in: [$tag] } } }
    ) {
      totalCount
      edges {
        node {
          fields {
            slug
          }
          excerpt(pruneLength: 160)
          html
          frontmatter {
            title
            date(formatString: "MMMM DD, YYYY")
            description
          }
        }
      }
    }
    site {
      siteMetadata {
        title
      }
    }
  }
`

記事の YAML Front Matter に記載した tag 情報を読めるようにする

gatsby-node.js を編集する。
tag をそのまま URL にして利用するためスペースが入ると何かと厄介なので tag の文字列をケバブケースに変換する。
ケバブケースはスペースをハイフンに変換した文字形態のこと。例)-□□-□-
語源は縦にするとケバブっぽいからみたい……なるほど!

const _ = require("lodash")

~~~~~~

exports.createPages = async ({ graphql, actions, reporter }) => {
  const { createPage } = actions

  // Define a template for blog post
  const blogPost = path.resolve(`./src/templates/blog-post.js`)
  const TagTemplate = path.resolve(`./src/templates/tags.js`)
  // Get all markdown blog posts sorted by date
  const result = await graphql(
    `
      {
        allMarkdownRemark(
          sort: { fields: [frontmatter___date], order: ASC }
          limit: 1000
        ) {
          nodes {
            id
            frontmatter {
              title
              description
              published
              tags
            }
            fields {
              slug
            }
          }
        }
        tagsGroup: allMarkdownRemark(limit: 2000){
          group(field: frontmatter___tags){
            fieldValue
          }
        }
      }
    `
  )

~~~~~~

   const tags = result.data.tagsGroup.group

    tags.forEach(tag => {
      createPage({
        path: `/tags/${_.kebabCase(tag.fieldValue)}/`,
        component: TagTemplate,
        context: {
          tag: tag.fieldValue,
        },
      })
    })
  }
}

各記事に該当する tag を表示する

blog-post.js を編集する。
日付の下に Mui の Crip を使って実装した。
tag 名はハッシュタグっぽく先頭に#をつけた。
クリップをクリックするとそれぞれの tag の記事一覧に飛ぶ。

import _ from "lodash"

;<header>
  <Typography variant="h5" component="div" itemProp="headline">
    {post.frontmatter.title}
  </Typography>
  <Typography variant="subtitle1" component="div" color="textSecondary">
    {" "}
    {post.frontmatter.date}{" "}
  </Typography>
  <Stack
    direction="row"
    justifyContent="flex-start"
    alignItems="center"
    spacing={1}
  >
    {post.frontmatter.tags.map(tag => {
      return (
        <Chip
          label={`#` + tag}
          component="a"
          href={`/tags/${_.kebabCase(tag)}/`}
          style={{ textDecoration: "none" }}
          clickable
        />
      )
    })}
  </Stack>
</header>

tag が設定されていない記事で下記のようなエラーが出た。


failed Building static HTML for pages - 2.685s
error Building static HTML failed for path "/posts/p26/"
  43 |               spacing={1}
  44 |             >
> 45 |               {post.frontmatter.tags.map(tag => {
     |                ^
  46 |                 return (
  47 |                   <Link to={`/tags/${_.kebabCase(tag)}/`}>
  48 |                     <Chip label={tag} />

  WebpackError:TypeError: Cannot read property 'map' of null (from plugin: gats  by-plugin-material-ui)

tag が設定されていないことを考慮して編集した。

{
  post.frontmatter.tags?.map(tag => {
    return (
      <Chip
        label={`#` + tag}
        component="a"
        href={`/tags/${_.kebabCase(tag)}/`}
        style={{ textDecoration: "none" }}
        clickable
      />
    )
  })
}

まとめ

開設当初からやりたい機能が実装ができて満足した。
tag が乱立するとあまり意味を成さないので運用面では課題が残る。
現在の更新ペースであればさほど問題にならないが、ループの回数が増えたためかビルド時間が長くなった。

参考

GatsbyJSで作っているブログにタグ機能を導入した

はじめに ギフティ Advent Calendar 2019 - Qiita の1日目の記事。このブログにタグ機能を導入したので、その時の手順を解説する。基本的に 公式の記事 に従えば良いのだが、差分が分かりづらいのと、自分で理解した内容を補足したいので、記事にした。 前提 このブログはGatsby公式のgatsby…

キクナントカドットコム

GatsbyJSで作っているブログにタグ機能を導入した

おしまい



定例MG研修に参加してきた
May 11, 2022February 20, 2024

MG 研修とは マネジメントゲームという会社経営ゲームで、資本金 300 万円を元手に企業して資本を増やすことを目指す。 題材が工業、商業などいろいろなバージョンがあるようだが、私の参加したのは e コマース版と呼ばれる ネットショップを経営する形のゲームだった。 MG 研修は巷で、25 期で会計がわかる。50…

Continue reading...
sakakinox

Written by sakakinox
Server enginier

Copyright © sakakinox.net 2021-2024.