Judo Paku
Judo Paku

Reputation: 51

Creating a Second Blog Template on Gatsby

I am trying to create a second blog template on my Gatsby site. I am only starting to learn Javascript so I need help with the gatsby-node.js file.

I tried to duplicate each lines of functions to create a second blog template essentially, but failed each time so if someone can walk through my code and provide the correct functions it would be very helpful.

I currently have an "articles" template which works successfully. I want to create a second "projects" template.

This is my current gatsby-node.js file that calls the "articles" blog template only.


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

// Look at every node when it is created
exports.onCreateNode = ({node, getNode, actions}) => {
  // Check for markdown nodes
  const { createNodeField } = actions;
  if(node.internal.type === 'MarkdownRemark') {
    // Create a slug out of the markdown filepath name
    const slug = createFilePath({
      node,
      getNode,
      basePath: 'articles'
    });
    // Add the newly created slug to the node itself
    createNodeField({
      node,
      name: 'slug',
      value: `/article${slug}`
    });
  }
};

exports.createPages = ({ graphql, actions }) => {
  const { createPage } = actions
  return new Promise((resolve, reject) => {
    graphql(`
    {
      allMarkdownRemark {
      edges {
        node {
        fields {
          slug
        }
        }
      }
      }
    }
    `).then(result => {
    result.data.allMarkdownRemark.edges.forEach(({ node }) => {
      createPage({
      path: node.fields.slug,
      component: path.resolve(`./src/templates/article.js`),
      context: {
        // Data passed to context is available in page queries as GraphQL variables.
        slug: node.fields.slug,
      },
      })
    })
    resolve()
    })
  })
  };


How can I configure this file to call for a second blog template ("projects")?

Upvotes: 3

Views: 220

Answers (1)

timmysmalls
timmysmalls

Reputation: 143

To start off you need to retrieve both your articles, as well as your projects in the graphql query you got. You can use a colon to rename a part of your query. I added articles: before your allMarkdownRemark. In the resulting JSON the allMarkdownRemark will now be called 'articles'. You can see this in the forEach loop that now iterates over 'result.data.articles'. I also added a query for the 'projects' entity you want. Renaming them to articles and projects allows us to easily iterate over every article and every project, and create the correct pages for these entities.

The projects query is now a carbon copy of your articles query. In reality you should provide a query here that returns only the projects, but I don't know where you have those. Then create separate loops for the projects and the articles in which you create the correct pages.

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

    return new Promise((resolve, reject) => {
        graphql(`{
            articles: allMarkdownRemark {
                edges {
                    node {
                        fields {
                            slug
                        }
                    }
                }
            }
            projects: allMarkdownRemark {
                edges {
                    node {
                        fields {
                            slug
                        }
                    }
                }
            }
        }`).then(result => {

            // In this loop we only take the articles returned by the query, and
            // pass them to the src/templates/article.js component.
            result.data.articles.edges.forEach(({ node }) => {
                createPage({
                    // Rather than prepending every slug with '/article' in the
                    // createNodeField call, you can add it here to get more
                    // flexibility, and to allow different types to have different
                    // path prefixes.
                    path: `article/${node.fields.slug}`,
                    component: path.resolve(`./src/templates/article.js`),
                    context: {
                        slug: node.fields.slug,
                    },
                })
            })

            // In this loop we do the same thing, only with the projects this
            // time, and passing them to the project.js component I'm assuming
            // you have. I've prepended the path with 'project/' to separate
            // the projects from the articles.
            result.data.projects.edges.forEach(({ node }) => {
                createPage({
                    path: `project/${node.fields.slug}`,
                    component: path.resolve(`./src/templates/project.js`),
                    context: {
                        slug: node.fields.slug,
                    },
                })
            })

            resolve()
        })
    })
};

I added some indentation to make things a little easier to reason about. I may have made some syntax errors along the way, so beware of that.

I noticed that in your onCreateNode call, you set the slug of every markdown file to 'article/${slug}`. If your projects are also markdown files you should not do this, because it will also give your project markdown files slugs that start with '/article'. In the code above, I set the correct path in the createPage call, not in createNodeField.

One thing you have to bear in mind is that if you've got both your articles as well as your projects as markdown files, your query is going to have a rough time telling them apart. The way I usually fix this is by adding a 'type' field to the frontmatter. So in your case you'd add: type: article to the frontmatter of all your articles, and type: project to all your projects. The frontmatter part is the part at the top of your markdown file, between the three dashes lines:

---
type: article
name: something
etc, etc...
---

You can filter your markdown files like this:

graphql(`query {
      projects: allMarkdownRemark ( filter: {frontmatter: { type: { eq: "project" } } } ) {
        edges {
          node {
            fields {
              slug
            }
          }
        }
      }
    }`);

This will return only markdown files that have type project. Do the same thing for your articles. If you use Netlify CMS, you can add a hidden field to automatically add the correct type to help you filter.

You may want to consider changing the promise into an async/await call. This is optional, but in my opinion it makes things a lot easier to read. Check out gatsby's tutorial here https://www.gatsbyjs.org/tutorial/part-seven/#creating-pages for an example of async/await in the createPages API.

Upvotes: 3

Related Questions