重い腰を上げてタグ一覧、カテゴリ一覧を実装した。実は元々タグ、カテゴリといったメタ情報はこのブログにあったが、一覧ページを作成するところで手を止まってしまっていた。GatsbyJSを思い出すよいトレーニングになると思ったので、さくっと実装してみる。
記事ファイルにタグ、カテゴリを追加
このブログはMarkdownで書いているので、先頭のメタデータにタグとカテゴリーを文字列配列で書く。
md: title=md1--- 2path: "/path/to/the/file" 3date: "2021-mm-dd"" 4title: "タイトル" 5categories: ["カテゴリ1", "カテゴリ2"] 6tags: ["GatsbyJS"] 7type: "post" 8--- 9 10
コンポーネントを作成
記事一覧等に埋め込むための src/components/categories.js
, src/components/tags.js
コンポーネントを作成する。
src/components/tags.js
は categories.js
とほとんど一緒なので割愛。
js: title=src/components/categories.js1import React from "react" 2import { Link } from "gatsby" 3import kebabCase from "lodash/kebabCase" 4 5const Categories = categories => ( 6 <div class="categories"> 7 {(categories || []).map(category => ( 8 <Link to={`/categories/${kebabCase(category)}/`}> 9 <span class="post-category">{category}</span> 10 </Link> 11 ))} 12 </div> 13) 14 15export default Categories 16
一覧ページを作成
もととなるテンプレートファイルを作成する。
src/templates/CategoryList.js
、 src/templates/TagList.js
を作成した。
TagListはクエリ含めてCategoryListとほとんど同じなので割愛。
js: title=src/templates/CategoryList.js1 2import React from "react" 3import { Link, graphql } from "gatsby" 4import Categories from "../components/catetgories" 5import Tags from "../components/tags" 6 7const CategoryList = ({ data, pageContext }) => { 8 const { category } = pageContext 9 return ( 10 <h1>{category} の絞り込み結果</h1> 11 {data.allMarkdownRemark.edges.map(({ node }) => ( 12 <article class="row"> 13 <div class="row col-xs-12 col-sm-12 col-md-12 meta-container"> 14 <h1> 15 <Link to={node.fields.slug}>{node.frontmatter.title}</Link> 16 </h1> 17 <div class="row col-md-12 post-meta-data text-gi-med bg-gi-light"> 18 <div class="row col-md-12"> 19 <small> {Categories(node.frontmatter.categories)}</small> 20 <small> {Tags(node.frontmatter.tags)}</small> 21 </div> 22 </div> 23 </div> 24 </article> 25 ))} 26 ) 27} 28 29export const query = graphql` 30 query($category: String) { 31 allMarkdownRemark( 32 sort: { fields: [frontmatter___date], order: DESC } 33 filter: { frontmatter: { categories: { in: [$category] } } } 34 limit: 2000 35 ) { 36 totalCount 37 edges { 38 node { 39 frontmatter { 40 title 41 categories 42 tags 43 } 44 fields { 45 slug 46 } 47 } 48 } 49 } 50 } 51` 52export default CategoryList 53 54
JSX
前述で作成した src/components/categories.js
src/components/tags.js
を読み込む。
GraphQL クエリ
filter
で categories
の中から $category
をもつ記事のみを抽出しているのがポイント。
gatsby-node.js を修正
記事にカテゴリ、タグを追記し、一覧ページの作成も完了したら、 gatsby-node.js
ファイルを修正してビルド時にカテゴリ一覧ページ、タグ一覧ページを作成するように設定する。
js: title=gatsby-node.js1 2... // 省略 3 4const result = await graphql(` 5 { 6 allMarkdownRemark( 7 sort: { order: DESC, fields: [frontmatter___date] } 8 limit: 1000 9 ) { 10 nodes { 11 id 12 fields { 13 slug 14 } 15 } 16 } 17 tagsGroup: allMarkdownRemark(limit: 1000) { 18 group(field: frontmatter___tags) { 19 fieldValue 20 } 21 } 22 categoriesGroup: allMarkdownRemark(limit: 1000) { 23 group(field: frontmatter___categories) { 24 fieldValue 25 } 26 } 27 } 28`) 29 30... // 省略 31 32// タグ別記事一覧を生成する 33const tags = result.data.tagsGroup.group 34tags.forEach(tag => { 35 createPage({ 36 path: `/tags/${kebabCase(tag.fieldValue)}/`, 37 component: path.resolve(`./src/templates/TagList.js`), 38 context: { 39 tag: tag.fieldValue, 40 }, 41 }) 42}) 43 44// カテゴリー別記事一覧を生成する 45const categories = result.data.categoriesGroup.group 46categories.forEach(category => { 47 createPage({ 48 path: `/categories/${kebabCase(category.fieldValue)}/`, 49 component: path.resolve(`./src/templates/CategoryList.js`), 50 context: { 51 category: category.fieldValue, 52 }, 53 }) 54}) 55 56
GraphQLクエリでは、後半に tagsGroup: ...
と categoriesGroup:...
からなるクエリを追加している。
元々あった記事一覧を取得するクエリと比べ、何をしているのか最初はわからなかったが、普通に書こうとすると allMarkdownRemark()
と干渉してしまってクエリが書けないため、下記のようにすることで名前空間を分けることができるみたい。
aliasName: allMarkdownRemark() { someItem }
この場合、 allMarkdownRemark().someItem
ではなく aliasName.someItem
のように書けるとのこと。
ビルドして問題なく動作すれば完了である。
やってみて思ったこと
GraphQL、昔触ったときも今触ったときも違和感しかないが、SQLと違って取得した結果をJSのオブジェクトでそのまま取れるのはだいぶ楽かもしれない。速いし。
上記のクエリもまだ冗長な書き方をしている気がするので、複雑になるにつれて書き方を改めたほうがよさそう。
そろそろ自動目次作成や、SNSシェアボタンが欲しくなってきたのでその辺も調べて実装してみる。
/ 以上
よかったらシェアしてください!