2019-01-26に更新

GatsbyJSでカテゴリー機能を実装する

カテゴリーページにもっと自由を

カテゴリー一覧を作っていきたい。
GatsbyのStarterではGraphQLのGroup機能を使ってやっている人が多い。
今回はMarkdownを使ってある程度自由にカスタマイズできるように実装していきたい。
それには理由があって、僕のサイトの作り方だとカテゴリーページが上がる傾向にある。そこでユーザーを離脱させない工夫としてカテゴリーをmarkdownで自由にカスタマイズできるようにしておきたい。

作っていくもの

  • categoriesフォルダ
  • gatsby-node.js
  • /templete/categorypage.js
  • /components/categoryPostlist/

こんだけで実装できそうだ。
まず、/contents/categoies/food.mdと
/contents/categories/drink.mdを作成。

---
pagetype: "category"
categoryname: "食べ物"
categoryslug: "food"
---

---
pagetype: "category"
categoryname: "飲み物"
categoryslug: "drink"
---

YAMLで上記のように記載する。
カスタマイズしたければMarkdownで加工すればいいようにする。

gatsby-node.js

続いてgatsby-node.jsを書いていく前にGraphQLでどこにフィルターをかけるかを見てみる。

{
  allMarkdownRemark(filter: {frontmatter: {pagetype: {eq: "category"}}}) {
    edges {
      node {
        id
      }
    }
  }
}

こんな感じでフィルターかけるのが簡単そう。
同じところから引っ張る場合エラーが出るのでそれぞれ名前を付ける

{
  blogposts: allMarkdownRemark(sort: {fields: [frontmatter___date], order: DESC}, limit: 1000) {
    edges {
      node {
        fields {
          slug
        }
        frontmatter {
          title
        }
      }
    }
  }
  categories: allMarkdownRemark(filter: {frontmatter: {pagetype: {eq: "category"}}}) {
    edges {
      node {
        frontmatter {
          categoryslug
        }
      }
    }
  }
}

最終的には

const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)

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

  return new Promise((resolve, reject) => {
    const blogPost = path.resolve(`./src/templates/blog-post.js`)
    const categoryPage = path.resolve(`./src/templates/categorypage.js`)
    resolve(
      graphql(
        `
        {
          blogposts: allMarkdownRemark(sort: {fields: [frontmatter___date], order: DESC}, limit: 1000) {
            edges {
              node {
                fields {
                  slug
                }
                frontmatter {
                  title
                }
              }
            }
          }
          categories: allMarkdownRemark(filter: {frontmatter: {pagetype: {eq: "category"}}}) {
            edges {
              node {
                frontmatter {
                  categoryslug
                }
              }
            }
          }
        }
        `
      ).then(result => {
        if (result.errors) {
          console.log(result.errors)
          reject(result.errors)
        }

        // Create blog posts pages.
        const posts = result.data.blogposts.edges
        const categories = result.data.categories.edges

        posts.forEach((post, index) => {
          const previous =
            index === posts.length - 1 ? null : posts[index + 1].node
          const next = index === 0 ? null : posts[index - 1].node

          createPage({
            path: post.node.fields.slug,
            component: blogPost,
            context: {
              slug: post.node.fields.slug,
              previous,
              next,
            },
          })
        })

        categories.forEach((category) => {
          createPage({
            path: `/category/${(category.node.frontmatter.categoryslug)}`,
            component: categoryPage,
            context: {
              slug: category.node.frontmatter.categoryslug,
          }})
        })
      })
    )
  })
}

exports.onCreateNode = ({ node, actions, getNode }) => {
  const { createNodeField } = actions

  if (node.internal.type === `MarkdownRemark`) {
    const value = createFilePath({ node, getNode })
    createNodeField({
      name: `slug`,
      node,
      value,
    })
  }
}

こうなった。
次は、テンプレを作る

import rehypeReact from "rehype-react"
import Sample from "../components/Sample"
import Categoryposts from "../components/categoryposts"

const renderAst = new rehypeReact({
  createElement: React.createElement,
  components: { "sample": Sample }
}).Compiler

class CategoryPageTemplate extends React.Component {
  render() {
    const post = this.props.data.markdownRemark
    const siteTitle = this.props.data.site.siteMetadata.title
    return (
      <Layout location={this.props.location} title={siteTitle}>
        <SEO title={post.frontmatter.categoryname} />
        <h1>{post.frontmatter.categoryname}</h1>
        <div>{renderAst(post.htmlAst)}</div>
        <hr/>
        <Categoryposts category={post.frontmatter.categoryslug} />
      </Layout>
    )
  }
}

export default CategoryPageTemplate

export const pageQuery = graphql`
  query CategoryPageBySlug ($slug: String!){
    site {
      siteMetadata {
        title
        author
      }
    }
    markdownRemark (frontmatter:{categoryslug:{ eq: $slug}}) 
    {
        htmlAst
        frontmatter {
        categoryname
        categoryslug
      }
    }
  }
`

最終的にこうなった。
そして、categorylistを作る。
/src/components/categoryposts/index.js

import React from 'react'
import { StaticQuery, graphql } from 'gatsby'

const CategoryPosts = (props) => (
  <StaticQuery
    query={graphql`
    query{
        allMarkdownRemark{
          edges{
            node{
              frontmatter{
                pagetype
                category
                title
                date
              }
              excerpt
            }
          }
        }
      }
    `}

    render={(data) => {
      const postlists = data.allMarkdownRemark.edges
      const posts = postlists.filter((category)=>{
          return (category.node.frontmatter.category === props.category)
      })

      return (
      <div>
      {posts.map(({ node }) => {
          return (
            <div key={node.frontmatter.title}>
              <p>{node.frontmatter.date}</p>
              <p dangerouslySetInnerHTML={{ __html: node.excerpt }} />
            </div>
          )
        })}
      </div>
      );
    }}
  />
)
export default CategoryPosts

ポイントはStaticQueryを使って全部取得してからフィルタリングしているところ。
コンポーネントでGraphQLを使用する場合、普通のクエリではと動かない仕様になっている。
019-1.jpg
上手く表示できた!

Originally published at corylog.com

view_list [連載] ブロガー向けGatsby講座
第16回 GatsbyJSでiframeタグを使う
第17回 GatsbyJSでMrakdown内でコンポーネントを使う
第18回 GatsbyJSでカテゴリー機能を実装する
第19回 GatsbyJSにドロワーメニューを追加する
第20回 GatsbyJSに画像付きカードメニューを作成する

aocory

Crieitは個人で開発中です。 興味がある方は是非記事の投稿をお願いします! どんな軽い内容でも嬉しいです。
なぜCrieitを作ろうと思ったか

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください!

ボードとは?

関連記事

コメント