はじめに
本記事は https://tech-blog.yoshikiohashi.dev/posts/start-gatsby-blog-add-tags-application のクロスポスト記事になります。
この記事はGatsbyというヘッドレスCMS技術で構成されています。今回は「エンジニア初心者でもできる」を前提に以下の構成で記事を作成していこうと思います。
- Gatsby始めるまで
- GatsbyにShare機能、OGPタグをつける
- タグ機能、カテゴリ機能をつける(基礎編)
- タグ機能、カテゴリ機能をつける(応用編)
- GatsbyにTableOfContents(目次)をつける
- DarkModeをつける
内容
前回はGraphQLを使用して、データを取得したJsonデータをTemplateに当てはめて画面に表示させるところまで行いました。 データの流れを理解できたでしょうか?この流れを理解するとあとは簡単です。
今回は応用編です。
「実際にはタグだけのページって必要でしょうか?」私は必要ないと思います。できれば、ユーザとしては選んだタグと記事の一覧が見れたら使いやすいはずです。
こんなタグ一覧+記事一覧ページを作るところをゴールに目指しましょう。
まずはタグ一覧+記事一覧のテンプレートファイルの作成
最初にGraphQLのクエリーを完成させよう
ポイントになってくるのが**query TagsListTemplate($tag: String!)とtags: { glob: $tag }**のTemplateの引数の指定の仕方と使い方です。globはglobalの略でいわゆる何でも検索ができます。*を入れたらすべてのタグが見つかるようになります。
タグ一覧(/tags)では全タグを表示させたいので*が入ってきますね。
query TagsListTemplate($tag: String!) {
allMarkdownRemark(
filter: {
frontmatter: {
template: { eq: "post" },
draft: { ne: true },
tags: { glob: $tag } }
},
sort: { order: DESC, fields: [frontmatter___date] }
){
group(field: frontmatter___tags) {
fieldValue
totalCount
}
edges {
node {
fields {
slug
categorySlug
}
frontmatter {
title
date
category
description
socialImage
}
excerpt
}
}
}
}
あとは、記事を取得するクエリーとタグを取得するクエリーを記述しましょう。
group(field: frontmatter___tags) {
fieldValue
totalCount
}
edges {
node {
fields {
slug
categorySlug
}
frontmatter {
title
date
category
description
socialImage
}
excerpt
}
}
GraphQLを実際に http://localhost:8000/___graphql
で試してJsonが取得できたら次へ進みましょう。
次は、取得したJsonをComponentに当てはめていきます。
const TagsListTemplate = ({ data, pageContext }) => {
const tags = data.allMarkdownRemark.group;
const posts = data.allMarkdownRemark.edges;
return (
<div className={TagsList}>
<Tags tags={tags} selectedTag={pageContext.tag}/>
<Post posts={posts} /> // それぞれの記事を表示するコンポーネントに合わせて差し替えてください
</div>
);
};
export const query = graphql`
query TagsListTemplate($tag: String!) {
allMarkdownRemark(
filter: { frontmatter: { template: { eq: "post" }, draft: { ne: true }, tags: { glob: $tag } } },
sort: { order: DESC, fields: [frontmatter___date] }
){
group(field: frontmatter___tags) {
fieldValue
totalCount
}
edges {
node {
fields {
slug
categorySlug
}
frontmatter {
title
date
category
description
socialImage
}
excerpt
}
}
}
}
`;
export default TagsListTemplate;
ここまででGraphQLクエリーでタグと記事の一覧を取得するクエリーを書き、Componentに当てはめることまでしました。次はタグのコンポーネントの作成です。
タグ一覧を表示するコンポーネントパーツの作成
先程作成したTemplateから呼び出されるタグコンポーネントです。こちらの例ではカウント順にソートしていますが、してもしなくてもOKです。
また、引数にselectedTag
がありますが、選択している状態を表現したかったので表示しています。好みで変更してOKです。
const sortTotalCount = (tags) => orderBy(tags, ['totalCount', 'fieldValue'], ['desc']);
const Tags = ({ tags, selectedTag }: Props) => (
<div className={styles["Tags"]}>
{sortTotalCount(tags).map(tag => (
<li key={tag.fieldValue}>
<Link to={`/tags/${kebabCase(tag.fieldValue)}/`} className={selectedTag === tag.fieldValue ? styles['Tags--Selected'] : '' }>
{tag.fieldValue}
<span>{tag.totalCount}</span>
</Link>
</li>
))}
</div>
);
export default Tags;
gatsby-node.js に /tags/ と /tags/{tags} のルーティングを作成する
gatsby-node.js
まずはタグ一覧ページを登録します。
すべてのタグを表示するのでcontextに**{ tag: "*" }**を指定しています。
// Tags list
createPage({
path: '/tags',
component: path.resolve('./src/templates/tags-list-template.js'),
context: { tag: "*" }
});
タグが選ばれた場合のURLを登録していきます。
ポイントはcontextの**{ tag: tag.fieldValue }を指定している箇所です。これでURLが/tags/{tags}**だったときにタグ名がTemplateに引数として受け渡しされることになります。
const result = await graphql(`
{
allMarkdownRemark(
filter: { frontmatter: { template: { eq: "post" }, draft: { ne: true } } }
) {
group(field: frontmatter___tags) {
fieldValue
totalCount
}
}
}
`);
_.each(result.data.allMarkdownRemark.group, (tag) => {
const numPages = Math.ceil(tag.totalCount / postsPerPage);
const tagSlug = `/tags/${_.kebabCase(tag.fieldValue)}`;
for (let i = 0; i < numPages; i += 1) {
createPage({
path: i === 0 ? tagSlug : `${tagSlug}/page/${i}`,
component: path.resolve('./src/templates/tags-list-template.js'),
context: {
tag: tag.fieldValue
}
});
}
}
処理の流れを表すとこのような図になります。
gatsby build を再度実行
/tags のページに遷移してみましょう。タグ一覧とその記事が表示されましたか?
まとめ
いかがだったでしょうか?うまく設定できたでしょうか?ここまで理解できたら他の実装にも応用できますね。それでは次回の記事で。