#概要
テンプレートを自身でアレンジする記事の第二弾となります。
前回の、GoogleAnalyticsからビュー数を取得して使用する記事に続きまして、
今回はCMSから投稿されたマークダウンファイルからタグデータを取得して
コンポーネント内に一覧で表示させていきたいと思います。
#前提
今回のプロジェクトではGatsby.js
+ Github
+ NetlifyCMS
でプロジェクトを組んでいます。
#手順
以下の手順で説明をしていきます。
1.実装したいもの
2.マークダウン内のデータ構造の説明
3.GraphQLでクエリを実装
4.createPageで指定のパスにページを作る
5.コンポーネントに静的なクエリを持たせる方法
#1.実装したいもの
ホーム画面に投稿された記事に含まれるタグ一覧を配置し、そこからそのタグを含むページ一覧を見れるような
形にしたいと思います。
ワードプレスで生成されたブログなどでよく見かける、サイドにタグがズラっと並んでいるあの形ですね!
#2.マークダウン内のデータ構造の説明
説明で用いるマークダウンのディレクトリ構成とマークダウンファイルの内容は以下になります。
-content
├ assets
└ blog //ここに.mdを保存
---
templateKey: blog-post
title: ABC
description: 概要
tags:
- A
- B
- C
date: 2020-06-02T09:16:39.445Z
---
ABC
---
templateKey: blog-post
title: ABC
description: 概要
tags:
- B
- C
- D
date: 2020-06-02T09:16:39.445Z
---
BCD
Gatsby.jsプロジェクトではマークダウンで記述されたをJSで処理することができるため、
ファイルの先頭部分に処理に必要なデータを含ませておくことができます。
上記にあるように、A、B、CのタグをもったABC.md
とB、C、DのタグをもったBCD.md
の二つを使っていきます。
#3.GraphQLでクエリを実装
Gatsby.jsが早い理由として、ビルドで最適化されたpublicフォルダ内の静的なページを読み込むことが挙げられますが
その際に用いられるGraphQLについて使用方法を軽く説明します。
gatsby develop
で立ち上げたモックサーバーlocalhost:8000
もしくは127.0.0.1:8000
のルートの直後に/__graphQL
を追加すると、そのプロジェクトで利用できるクエリが検索できます。
左のExplorerタブ
で取得したい情報をクリックしていけば自動的にクエリが生成され、▶︎を押すとクエリのレスポンスを確認することができます。
今回取得したいのは全記事の情報ですのでallDownRemark
をクリックしてください。これは各記事の情報を配列に格納して返してくれるクエリです。
記事を上から新しい順にならべたいので、
sort
> fields
にfrontmatter__date
、sort
> order
にDESC
を設定してください。
また、各記事のパス、タイトル、タグが必要ですので
edges
> node
と進んでいただき
fields
の slug
と
frontmatter
の tags
, title
にチェックを入れてください。
そうすると、右側にクエリが自動生成されているのがわかると思います。
#4.createPageで指定のパスにページを作る
クエリで取得した記事内のデータを使って検索結果のページを生成してみましょう。
まずは、生成するページにどのタグの検索結果を表示してほしいのかの情報を渡して
生成してもらう追記をしていきます。gatsby-node.js
を開いてクエリを以下のように編集します。
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const blogPost = path.resolve(`./src/templates/blog-post.js`)
const result = await graphql(
`
{
allMarkdownRemark(
sort: { fields: [frontmatter___date], order: DESC }
limit: 1000
) {
edges {
node {
fields {
slug
}
frontmatter {
title
tags
}
}
}
}
}
`
)
if (result.errors) {
throw result.errors
}
const posts = result.data.allMarkdownRemark.edges
//追記
let tags = []
posts.forEach(edge => {
if (_.get(edge, `node.frontmatter.tags`)) {
tags = tags.concat(edge.node.frontmatter.tags)
}
})
tags = _.uniq(tags)
tags.forEach(tag => {
const tagPath = path.resolve(`/tags/${tag}`)
createPage({
path: tagPath,
component: path.resolve(`./src/templates/tags.js`),
context: {
tag: tag,
},
})
})
}
createPageのpathに生成するページのルートからの相対パスを、
componentに後述するテンプレートのパスを、
contextに生成する際の情報としてtagを入れています。
次に取得したデータを貼り付ける用のテンプレートを記述していきます。
-src
├ components
├ pages
└ templates //ここに保存してください。
import React from "react"
import { Link, graphql } from "gatsby"
import Layout from "../components/layout"
import SEO from "../components/seo"
const TestTemplate = ({ data, pageContext, location }) => {
const posts = data.allMarkdownRemark.edges
const title = data.site.siteMetadata.title
const tag = pageContext.tag
const postLinks = posts.map(post => (
<li key={post.node.fields.slug}>
<Link to={post.node.fields.slug}>
<h2 className="is-size-2">{post.node.frontmatter.title}</h2>
</Link>
</li>
))
console.log(data)
return (
<Layout location={location} title={`Tags | ${title}`}>
<SEO title={tag} />
<h1>
{tag}:検索結果 {data.allMarkdownRemark.totalCount} 件
</h1>
<ul>{postLinks}</ul>
<p>
<Link to="/tags/">Browse all tags</Link>
</p>
</Layout>
)
}
export default TestTemplate
export const tagPageQuery = graphql`
query tagPageQuery($tag: String) {
site {
siteMetadata {
title
}
}
allMarkdownRemark(
limit: 1000
sort: { fields: [frontmatter___date], order: DESC }
filter: { frontmatter: { tags: { in: [$tag] } } }
) {
totalCount
edges {
node {
fields {
slug
}
frontmatter {
title
}
}
}
}
}
`
gatsby-node.js
で渡されたcontextの情報を受け取り、クエリで記事を再検索しています。
リザルトはTestTemplate
にあるdataに格納されているのでそれを必要な形で配置しておいてください。
これでモックサーバーで該当するURL(この説明の場合はlocalhost:8000/tags/A
)に飛ぶと
検索結果が表示されているはずです。
#5.コンポーネントに静的なクエリを持たせる方法
これまでだけですと、検索結果(/tags/〇〇)のリンクを一々手動で追加しないといけないので
記事からタグ一覧をリンクとして表示するコンポーネントを制作していきます。
ですが、Pages以下のファイルではexportでクエリを使用できるのに対し、
コンポーネントではJSXの中にクエリを埋め込まなければ使えないという仕様があります。
それを踏まえて/src/components/
に新しいファイルを作り、以下を記述してください。
import React from "react"
import { kebabCase } from "lodash"
import _ from "lodash"
import { Link, graphql, StaticQuery } from "gatsby"
const categoryQuery = graphql`
query categoryQuery {
allMarkdownRemark(
sort: { fields: [frontmatter___date], order: DESC }
limit: 1000
) {
edges {
node {
fields {
slug
}
frontmatter {
title
tags
}
}
}
}
}
`
const CategoryContents = ({ data }) => {
const posts = data.allMarkdownRemark.edges
let tags = []
posts.forEach(edge => {
if (_.get(edge, `node.frontmatter.tags`)) {
tags = tags.concat(edge.node.frontmatter.tags)
}
})
tags = _.uniq(tags)
let tagsList = []
tags.forEach(tag => {
let count = 0
posts.forEach(edge => {
edge.node.frontmatter.tags.forEach(_tag => {
if (_tag == tag) {
++count
}
})
})
tagsList.push({
tag: tag,
totalCount: count,
})
})
const categoryContents = (
<section>
<p
style={{
textAlign: `center`,
marginLeft: `auto`,
marginRight: `auto`,
}}
>
カテゴリー
</p>
<div>tags</div>
<ul>
{tagsList.map(
list => (
<li key={list.tag + `tag`}>
<Link to={`/tags/${kebabCase(list.tag)}/`}>
{list.tag}({`${list.totalCount}`})
</Link>
</li>
)
)}
</ul>
</section>
)
return categoryContents
}
export default function Category() {
const category = (
<StaticQuery
query={categoryQuery}
render={data => <CategoryContents data={data} />}
/>
)
return category
}
クエリとテンプレートを別々に記述し、最後に<StaticQuery />
内で各々のプロパティに代入して
exportしてあげれば、
このコンポーネントをLayout.js
などでimportするだけで一覧のリンクが表示されます。
これはリスト形式で表示させるだけですので、スタイルなどは各自でいい感じに仕上げていただけると幸いです。