This article is part four in an ongoing series on creating a blog site similar to this one. It covers building the actual list of blog posts and displaying the posts themselves.
For Part 3 which talks about setting up the Gridsome project and integrating NetlifyCMS, see Creating The Project.
Alright, in Part 3 of this series we created our Gridsome project and integrated NetlifyCMS, defining our blog content structure in the process. Let's actually show the blog posts already!
Let’s start by reorganizing our template a bit. We’re going to leave the About Us page as is since that’s not the important part of this blog. We’ll re-purpose the Home page to be our list of blogs. Later we’ll create the page for displaying an individual post.
Open up the src/pages/Index.vue file. Go ahead and delete everything in the template. You can also change the title within the metaInfo section of the script to something like “My Blog”. And finally, delete everything within the style tags. You should be left with something like:
<template>
</template>
<script>
export default {
metaInfo: {
title: 'My Blog'
}
}
</script>
<style>
</style>
After the template, add another section called page-query. This is a special Gridsome section where we’re going to define out GraphQL interface. Gridsome is GraphQL driven at heart so it has these special constructs in place.
<page-query>
</page-query>
Next, add a query section inside there
<page-query>
query Posts {
}
</page-query>
There’s nothing particularly important here, we’re just defining a query named Posts (name can be anything). Finish it off as follows (don’t worry we’ll go through it all after).
<page-query>
query Posts {
blogs: allPost {
edges {
node {
id
title
summary
author
date
banner
path
}
}
}
}
</page-query>
Here’s what each piece does:
So that’s our GraphQL definition out of the way.
This is going to be a work of art so wait for it…
<Layout>
<h1>My Blog Posts</h1>
<div v-for="post in $page.blogs.edges" :key="post.node.id">
<h2>{{ post.node.title }}</h2>
<p>{{ post.node.summary }}</p>
</div>
</Layout>
I know right, that must have taken me forever! Seriously though, all we’re really doing is looping through the list of blogs ($page is a Gridsome property for accessing the GraphQL) and the rest is rest is just accessing the collection of nodes and displaying a div with an h2 and paragraph for each post. As in all Vue v-for statements, you want to have a :key property with a unique value. We could use the index with something like:
<div v-for="(post, index) in $page.blogs.edges" :key="index">
but since each blog post has a unique ID, we should use that.
Now, if you run it again with
yarn develop
or
npm run develop
You should see something like:
Just a quick aside to setup sorting so they appear in descending order of when they were created (newest to oldest).
In your Index.vue file, change the line in the
blogs: allPost {
to
blogs: allPost(sortBy: "date") {
Save the file and it should refresh, showing the posts in the order 3, 2, 1. At least that’s the order my data should appear in.
Okay, this is where things get a little bit weird. Up until now, all of our pages have been pre-determined by our site design. For example, there will always only be one page listing the blogs so we can define its route ahead of time. For the posts themselves, we need to define the routes when we compile. In order to create these routes, Gridsome requires that pages with dynamic routes be defined in the templates folder and not in the pages folder. The name of the template is also important. The correct file for our purposes will be Post.vue since our collection is named Post (from typeName in gridsome.config.js as mentioned previously).
So let’s add a Post.vue file to the src/templates folder.
Here’s the template section for the file
<Layout :title="$page.blog.title">
<Header :post="$page.blog" />
<div>
<div v-html="$page.blog.content" />
</div>
</Layout>
</template>
Most of this is pretty straightforward with the possible exception of the v-html directive in the content div. NetlifyCMS returns the content of the post Markdown as HTML. It basically goes through the Markdown and converts it to HTML, replacing things like headers with h1, h2, etc. tags and things like that. This makes it much easier for us to display. v-html is a Vue directive that renders the content as pure HTML in the div. If we were to just bind to $page.blog.content in the content of the div with something like:
<div>{{ $page.blog.content }}</div>
we would get an ugly mess since Vue by default will HTML encode string data. This is a security measure. So basically, by using v-html, we’re telling Vue that we know what we’re doing and stop messing with our stuff!
Next, let’s add the
<page-query>
query BlogPost ($path: String!) {
blog: post (path: $path) {
title
content
banner
author
date
keywords
summary
}
}
</page-query>
This is similar to what we saw before with a few key differences. The whole purpose of the $path bits is to pull the correct post for the page. As near as I can figure out, the ($path: String!) in the query line tells the query to reference the $path variable (created by Gridsome). The (path: $path) bit in the blog: post line tells the query itself to use the path to find the correct post. What you’re not seeing here is that the blog entity contains a path property that it can compare against. I know, simple, right? This is still one area where my understanding is fuzzy but it works.
The rest of that is pretty much as we saw before.
Now let’s add the script section:
<script>
export default {
metaInfo() {
return {
title: this.$page.blog.title
}
}
};
</script>
Yep, that’s it. All we’re doing is setting the page metaInfo title field to the title of the blog. This is used by Gridsome and doesn’t really impact what we’re doing here.
Now we just need to link to this page somehow! Open up your src/pages/Index.vue file. Change the template to look like this
<template>
<Layout>
<h1>My Blog Posts</h1>
<g-link v-for="post in $page.blogs.edges" :key="post.node.id" :to="post.node.path">
<h2>{{ post.node.title }}</h2>
<p>{{ post.node.summary }}</p>
</g-link>
</Layout>
</template>
All we changed was the div became a g-link with a :to directive. g-link is a Gridsome link. It’s like a regular HTML a tag except that it tells Gridsome a few extra things so it can build the link url appropriately. The :to directive tells Gridsome where to find the path to use for the link. That’s it.
Now if you look at your site in the browser you should see that the list of blogs is now links and clicking on your test blog … drum rolllllll … opens a page like this:
SUCCESS! We now have a blog site! Granted, not a pretty one but hey, this isn’t a CSS course.
In the next post in the series we'll be talking about adding meta information for SEO since a blog isn't very effective if no-one can find it!