Raymond Camden has a great article that provides a walkthrough on how to accomplish something very similar. The difference is we will be rendering the previous and next post’s title and description.
In a Harp Boilerplate, Kenneth Ormandy provides article navigation in a Jade template. We will use that as a base for our article navigation in EJS. Skip to the final code.
Some Example Data
In our articles folder we would have something like the JSON below in a _data.json
file.
{
"article-three": {
"title": "Article Three",
"date": "2015-01-03",
"modified": null,
"desc": "Description three.",
"published": true,
"art": null
},
"article-two": {
"title": "Article Two",
"date": "2015-01-02",
"modified": null,
"desc": "Description two.",
"published": true,
"art": null
},
"article-one": {
"title": "Article One",
"date": "2015-01-01",
"modified": null,
"desc": "Description one.",
"published": true,
"art": null
}
}
In our work folder we would have something like the JSON below in a _data.json
file.
{
"work-three": {
"title": "Work Three",
"date": "2015-01-03",
"modified": null,
"desc": "Description three.",
"published": true,
"art": null
},
"work-two": {
"title": "Work Two",
"date": "2015-01-02",
"modified": null,
"desc": "Description two.",
"published": true,
"art": null
},
"work-one": {
"title": "Work One",
"date": "2015-01-01",
"modified": null,
"desc": "Description one.",
"published": true,
"art": null
}
}
Setting up the Data
Having a reusable piece of navigation is helpful. For example, you have a site that has a blog with articles and another part with a portfolio of work. Both parts of the site could use the same navigation system.
To extend Kenneth’s boilerplate code we can abstract that concept to collections of posts
. In the EJS that is responsible for generating an article: we include the post partial, create an array that contains all of our articles, and include the navigation partial:
<% include ../_includes/post %>
<% var posts = []; %>
<% for(var slug in public.articles._data){ %>
<% var post = public.articles._data[slug] %>
<% if(post.date && post.published !== false) { %>
<% post.slug = slug; %>
<% posts.push(public.articles._data[slug]);%>
<% } %>
<% } %>
<% include ../_includes/post-nav %>
For our portfolio pieces we write almost the same thing in the Work partial as we did in the Articles partial. However this time, we populate the posts
array with our collection of work
:
<% include ../_includes/post %>
<% var posts = []; %>
<% for(var slug in public.work._data){ %>
<% var post = public.work._data[slug] %>
<% if(post.date && post.published !== false) { %>
<% post.slug = slug; %>
<% posts.push(public.work._data[slug]);%>
<% } %>
<% } %>
<% include ../_includes/post-nav %>
You will notice that we are wrapping posts.push
in a conditional. This condition says that we only push a post
into our posts
array if the it has a date
defined and is set to published
.
Pass that Data
In the case of EJS, let’s first sort the posts
by their date using the method from Kenneth’s Jade template:
<% posts.sort(function(a,b){ a = new Date(a.date); b = new Date(b.date); return b<a?-1:b>a?1:0; }).slice(0, 10) %>
The array of posts is now sorted by the date published, which would be defined in the _data.json
located in the articles
and work
folders respectively.
Now we need to figure out which post is currently being rendered so we can display the previous post and the next post. We setup a for
loop to go through our posts
array. In that loop we leverage the current
object in Harp. If the slug of the index matches the current.source
we create two variables.
The prev
variable is -1
of index from our current page index and the next
variable is +1
from our current page index.
<% posts.sort(function(a,b){ a = new Date(a.date); b = new Date(b.date); return b<a?-1:b>a?1:0; }).slice(0, 10) %>
<% for(var i = 0; i < posts.length; i++) { %>
<% if(posts[i].slug == current.source) { %>
<% var prev = posts[i-1] %>
<% var next = posts[i+1] %>
<!-- Our Markup Will Go Here -->
<% } %>
<% } %>
The Navigation Partial
We are almost done. Now we have the previous post data and next post data stored in their respective variables. It’s time to pass that data into some markup:
<% posts.sort(function(a,b){ a = new Date(a.date); b = new Date(b.date); return b<a?-1:b>a?1:0; }).slice(0, 10) %>
<% for(var i = 0; i < posts.length; i++) { %>
<% if(posts[i].slug == current.source) { %>
<% var prev = posts[i-1] %>
<% var next = posts[i+1] %>
<nav>
<% if(prev) { %>
<h2><%- prev.title %></h2>
<p><%- prev.desc %></p>
<a href="/<%= current.path[0] %>/<%= prev.slug %>">Previous</a>
<% } %>
<% if(next) { %>
<h2><%- next.title %></h2>
<p><%- next.desc %></p>
<a href="/<%= current.path[0] %>/<%= next.slug %>">Next</a>
<% } %>
</nav>
<% } %>
<% } %>
We setup two conditions: one for the previous post and the other for the next post. If there is a prev
post we output the post’s title, description, and a link to the previous post in the nav
markup. We do the same thing for the next post’s markup, but instead we use the next
post data.
In the end, we are using one navigation partial for articles and work using Harp’s _data.json
metadata system and current
object.