Published on

タグの機能を実装してみる

Authors
  1. 公式サイトを参考に一通り実装してみる -> タグ毎の一覧ページはできたけどちょっと違う

  2. カテゴリの実装例のブログがあったので真似してみる。graphQL の書き方がおかしくてうまくいかない。以前はそれで良かったのかも?そのブログ自体にも実装したと書いてあったけど機能していなかった

  3. タグ機能を導入したというブログを見つけたので参考にしてみる。

  4. 自分の gatsby-node に合わせて記述してみる。Top ページのブログの表示と混ざっているけど、下記のようにしてみた。

    title=gatsby-node.js
    const path = require(`path`)
    const _ = require("lodash")
    const { createFilePath } = require(`gatsby-source-filesystem`)
    
    exports.createPages = async ({ graphql, actions, reporter }) => {
    const { createPage } = actions
    const blogPost = path.resolve(`./src/templates/blog-post.js`)
    const tagTemplate = path.resolve(`./src/templates/tags.js`)
    
    const result = await graphql(
        `
        {
            allMarkdownRemark(
            sort: { fields: [frontmatter___date], order: ASC }
            limit: 1000
            ) {
            nodes {
                id
                fields {
                slug
                }
            }
            }
            tags: allMarkdownRemark(limit: 1000) {
            group(field: frontmatter___tags) {
                fieldValue
            }
            }
        }
        `
    )
    
    if (result.errors) {
        reporter.panicOnBuild(
        `There was an error loading your blog posts`,
        result.errors
        )
        return
    }
    
    const posts = result.data.allMarkdownRemark.nodes
    if (posts.length > 0) {
        posts.forEach((post, index) => {
        const previousPostId = index === 0 ? null : posts[index - 1].id
        const nextPostId = index === posts.length - 1 ? null : posts[index + 1].id
    
        createPage({
            path: post.fields.slug,
            component: blogPost,
            context: {
            id: post.id,
            previousPostId,
            nextPostId,
            },
        })
        })
    }
    const tags = result.data.tags.group
        tags.forEach(tag => {
        createPage({
            path: `/tags/${_.kebabCase(tag.fieldValue)}/`,
            component: tagTemplate,
            context: {
            tag: tag.fieldValue,
            },
        })
        })
    }
    

    今回のタグに関して記述した箇所は、

    title=gatsby-node.js
    const _ = require("lodash")
    
    const tagTemplate = path.resolve(`./src/templates/tags.js`)
    
    tags: allMarkdownRemark(limit: 1000) {
            group(field: frontmatter___tags) {
                fieldValue
            }
        }
    
    const tags = result.data.tags.group
        tags.forEach(tag => {
        createPage({
            path: `/tags/${_.kebabCase(tag.fieldValue)}/`,
            component: tagTemplate,
            context: {
            tag: tag.fieldValue,
            },
          })
        })
    

    です。
    まだまだ理解が浅いので、公式のハウツーガイドを読み込まなきゃなぁ。

  5. 次にタグに関して表示するためのテンプレートページの作成。自分は src/templates に tags.js を作成。

    title=tags.js
    import React from "react"
    import { Link, graphql } from "gatsby"
    
    import Bio from "../components/bio"
    import Layout from "../components/layout"
    import SEO from "../components/seo"
    
    const Tags = ({ location, pageContext, data }) => {
        const post = data.markdownRemark
        const siteTitle = data.site.siteMetadata?.title || `Title`
    
        return (
            <Layout location={location} title={siteTitle}>
                <SEO title={pageContext.tag}  />
                <h1>
                    {pageContext.tag} ({ data.allMarkdownRemark.totalCount})
                </h1>
                <ol style={{ listStyle: `none` }}>
                    {data.allMarkdownRemark.edges.map(({ node }) => {
                        return (
                            <li key={ node.fields.slug }>
                                <article className="post-list-item" ></article>
                                <header>
                                <h2>
                                    <Link to={node.fields.slug} itemProp="url">
                                    <span itemProp="headline">{node.frontmatter.title}</span>
                                    </Link>
                                </h2>
                                    <small>{post.frontmatter.date}</small>
                                </header>
                                <section>
                                    <p
                                        dangerouslySetInnerHTML={{
                                        __html: node.frontmatter.description || post.excerpt,
                                        }}
                                        itemProp="description"
                                    />
                                </section>
                            </li>
                        )
                    })}
                </ol>
                <hr />
                <footer>
                    <Bio />
                </footer>
            </Layout>
        )
    }
    
    export default Tags
    export const pageQuery = graphql`
        query($tag: String) {
            allMarkdownRemark(
                limit:1000
                sort: { fields: [frontmatter___date], order: DESC }
                filter: { frontmatter: { tags: { in: [$tag] } } }
            ){
                totalCount
                edges {
                    node {
                    fields {
                        slug
                    }
                    frontmatter {
                        title
                        tags
                        date
                        description
                    }
                    }
                }
            }
            markdownRemark {
                id
                excerpt(pruneLength: 160)
                html
                frontmatter {
                title
                date(formatString: "MMMM DD, YYYY")
                description
                tags
                }
            }
            site {
                siteMetadata {
                title
                }
            }
        }
    `
    

    今後変わっていく可能性が高いけど、今の所このサイトの作りとしてはこんな感じです。
    トップページのテンプレートを参考にしてみました。
    ですが global であてている css が効かなくて h1 の色が黒い…
    おいおい読み込んでみますが、誰か教えて下さい〜!

  6. このテンプレートを表示させるために、各記事にタグを表示させます。自分はスターターをいじっているので blog-post.js というテンプレートで各投稿記事を表示させています。なのでそこに

title=blog-post.js
import _ from "lodash"

<div className="tag-name-wrapper">
        {tags.map(tag => {
        return <Link className="blog-tag-name" to={`/tags/${_.kebabCase(tag)}/`}>{tag}</Link>
        })}
</div>

div で囲っているのは色つけてるだけなんでお気になさらず。
lodash と kebabCase を理解していないので、これもそのうち勉強しなきゃ。
これで各投稿にタグが表示されて、そのタグのリンクをクリックすると gatsby-node に書かれた createPage によって tag.js で書いた「タグの記事一覧」が表示されます。


最初はカテゴリーを作成して、色々な投稿をした際に整理しやすくしようとしたんだけど、公式でもタグのことについての記事だったのでひとまずこれでいいかなと。
今後必要になったら同じ要領でやればいいかな。 少し graphQL について理解が進んだので良しとします。 ほいじゃ!