ことばを学ぶ　LEARN GATSBY 週間 #4日目

こんにちは！

前回は記事の一覧ページを作成しました.

一日目は[こちら](https://qiita.com/nibu0215/items/db2cae3b813952208adc)

今回は、記事一覧ページと個々の記事のつながりを作りたいと思います.
#slugを作る
つながりのキーワードはずばり、`slug` です.
`slug`とは、個々の記事を区別する `ID` です.

ここではわかりやすく、 `slug` = `filename` (記事のファイル名) と設定します.
(記事ファイルは `マークダウン` で書かれているので、拡張子が `.md` です.)

`GraphQL playground` にて以下を実行してみましょう.

```
query {
  allMarkdownRemark {
    edges {
      node {
        internal {
          type
        }
       fileAbsolutePath
      }
    }
  }
}
```
すると右側に、`type` が `"MarkdownRemark"` 、その絶対パス (fileAbsolutePath) が表示されます.

よって、 `slug` を取得するには、今回の場合、記事のファイル名を取得するには、絶対パスの最後を抜き取って、拡張子を消してしまえばいいわけです.
やってみましょう. ページ関係の設定は `gatsby-node.js`というファイルに書きます.
 幸いにも簡単にできるメソッドがあります ! それが `path.basename()`です. 
[参考サイト](https://www.w3schools.com/nodejs/met_path_basename.asp)

```gatsby-node.js
const path = require('path')

module.exports.onCreateNode = ({ node , actions }) => {
    const { createNodeField } = actions
    if (node.internal.type === 'MarkdownRemark') {
        const slug = path.basename(node.fileAbsolutePath , '.md' )

        createNodeField({
            node,
            name: 'slug',
            value: slug
        })

    }

}
```
本当に `slug` ができているか、開発用サーバーを再び立ち上げて、`graphQL`で確認してみましょう.

```
query {
  allMarkdownRemark {
    edges {
      node {
        fields{
          slug
        }
    	}
  	}
	}
}
```
実行ボタンを押すと、記事のファイル名が`slug`としてずらーっと表示されます.
この `slug` を利用して、新しいページを作ってみましょう !
#ページ作成
新しいページを作成するには `pages` フォルダにファイルを作れば、そのファイル名をパスとしてページが作成できました. しかし記事が何千もある場合はそのやり方では苦痛です.

そこで、`Gatsby` では `templates` フォルダを作り、そこに記事の テンプレートを作成し、さきほど作った `slug` と `記事のデータ` を対応付けることによって、テンプレートを一つ作るだけで、記事を好きなだけ表示させることができるんです.
やってみましょう.

`src` フォルダ内に `templates` フォルダを作成し、`blog-template.js` ファイルを追加します.
`slug` と対応づけるという意味を具体的に `graphQL` を使ってみてみましょう.
以下のクエリに加え、

```
query ( $slug: String! ) {
  markdownRemark (fields: { slug: { eq: $slug } }) {
    frontmatter {
      title
      date(formatString: "MMMM DD, YYYY")
    }
    html
  }
}
```
`GraphQL` 画面左下の `QUERY VARIABLES` に以下を加えると、

```
{
  "slug": "how"
}
```
`slug` が `how` である記事のデータが引き出すことができます. 
ポイントはこの部分

```
(fields: { slug: { eq: $slug } })
```
`eq` は `equal` を意味し、`$slug` は変数を表しています. ここでは `QUERY VARIABLES` にて、`"how"` と指定しているので、ここの変数を変えることで、ほしい記事のデータを引き出すことができます.
これをすべての `slug` でやってみましょう.
設定は `gatsby-node.js` でします.

```gatsby-node.js
module.exports.createPages = async ({ graphql , actions }) => {
    const { createPage } = actions
    const blogTemplate = path.resolve('./src/templates/blog-template.js')

    const res = await graphql(`
        query {
            allMarkdownRemark {
                edges {
                    node {
                        fields {
                            slug
                        }
                    }
                }
            }
        }
    `)

    res.data.allMarkdownRemark.edges.forEach( ({ node }) => {
        createPage({
            component: blogTemplate ,
            path: `/bloglist/${node.fields.slug}` ,
            context: {
                slug: node.fields.slug
            }
        })
    })

}
```
これがやっていることを順に説明すると、

「 まずさきにクエリのデータを `res` に格納し、各記事ごとに (`forEach` メソッドを利用して)
`createpage` 関数で、component(どのテンプレートを使う？)、path(パスは？) 、context(ここで定義した `slug` が さきほど `GraphQL ` 画面左下の `QUERY VARIABLES` で定義した `slug` と同じ意味になる)　を設定しています 」というわけです.

つまり、あらかじめ各記事ごとに `slug` を定義しておき、指定されたパスに、`slug` との対応付けにより引き出されるデータを、指定されたテンプレートに表示させています.

いまから、上の文章の「 `slug` との対応付けにより引き出されるデータ」を実際にクエリで、`blog-template.js` のなかに書き、それを表示させたいと思います !

```blog-template.js
import React from 'react'
import { graphql } from 'gatsby'
import Layout from '../Layouts/common'

export const query = graphql`
    query ( $slug: String! ) {
      markdownRemark (fields: { slug: { eq: $slug } }) {
        frontmatter {
            title
            date(formatString: "MMMM DD, YYYY")
        }
        html
    }
}
`

const Blog = (props) => {
    return (
        <Layout>
            <h1>{props.data.markdownRemark.frontmatter.title}</h1>
            <p>{props.data.markdownRemark.frontmatter.date}</p>
            <div dangerouslySetInnerHTML={{ __html: props.data.markdownRemark.html}}></div>
        </Layout>
    )
}

export default Blog
```
`slug` との対応付けっていうところが、

```
(fields: { slug: { eq: $slug } })
```
ですね !　これでパスを指定すると、その記事に飛べるようになりました！

ページを作っているというより、**一枚のページの情報を変えて、あたかも複数のページが存在している風にみせている** という点が、`createPage` 関数の良さですね ! この考え方はまた後に出てきます.

さて、`bloglist` ページにリンクを追加してみましょう !

#記事一覧ページから記事をみる

```bloglist.js
import React from 'react'
import { Link , graphql , useStaticQuery } from 'gatsby'
import Layout from '../Layouts/common'

const BlogList = () => {
    const data = useStaticQuery(graphql`
        query {
            allMarkdownRemark {
                edges {
                    node {
                        frontmatter {
                            title
                            date (formatString: "MMMM DD, YYYY")
                        }
                        excerpt
                        fields{
                            slug
                        }
                    }
                }
            }
        }
    `)
    
    return (
        <Layout>
            <h1>This is BlogList  </h1>
            <ol>
             {data.allMarkdownRemark.edges.map( ({node}) => {
                return (
                    <li key={node.fields.slug}>
                        <Link to={`/bloglist/${node.fields.slug}`} >
                            <h2>{node.frontmatter.title}</h2>
                            <p>{node.frontmatter.date}</p>
                        </Link>
                    </li>
                )
             })}
            </ol>
        </Layout>
    )
}

export default BlogList
```
これで記事一覧ページから、各記事を見ることができるようになりました !!

※ `header.module.scss` に以下を追加してください. 
スクロールの出現によるレイアウト崩れをなくします.

```header.module.scss
body {
    overflow-y: scroll;
  }
```

ここまでが一応チュートリアルの基本です !

次回から、タグ機能、ブログリストページの分割などを学びたいと思います.

誤字・脱字・コード間違い等ありましたら、コメントお待ちしております.

ここまで読んでくださってありがとうございました.