How to create a post list from specific tags
Sometimes you may need to display a list of posts from a specific tag or tags into an article. It's possible to do this via a shortcode: [posts tags="x,y,z" count="5"] . To implement this we have to run through a few coding steps, as follows:
We enable the createContentStructure option in the rendering section of the config. json file:
"renderer": {
"relatedPostsNumber": 3,
"createContentStructure": true,
...
},
Now we have to create/add the following code to our helpers.js file:
/*
* Custom theme helpers for Handlebars.js
*/
let themeHelpers = {
parseShortcode: function(postText, content) {
let allTags = content.data.website.contentStructure.tags;
let matches = postText.match(/\[posts tags="(.*?)" count="(.*?)"\]/gmi);
if(!matches) {
return postText;
}
for(let i = 0; i < matches.length; i++) {
let tags = matches[i].match(/tags="(.*?)"/);
let count = matches[i].match(/count="(.*?)"/);
count = count[1];
let tagNames = tags[1].split(',');
let posts = [];
let output = '';
for(let j = 0; j < tagNames.length; j++) {
for(let k = 0; k < allTags.length; k++) {
if(allTags[k].slug === tagNames[j]) {
for(let l = 0; l < allTags[k].posts.length; l++) {
posts.push(allTags[k].posts[l]);
}
}
}
}
posts = [...new Set(posts)];
posts.sort((a, b) => parseInt(a.createdAt) - parseInt(b.createdAt));
posts = posts.slice(0, count);
if(posts.length) {
output = '<ol>';
for(let j = 0; j < posts.length; j++) {
output += '<li><a href="' + posts[j].url + '">' + posts[j].title + '</a></li>';
}
output += '</ol>';
}
postText = postText.replace(matches[i], output);
}
return postText;
}
};
module.exports = themeHelpers;
If your existing helpers file includes other bits of code then you'll have to combine them as follows:
/*
* Custom theme helpers for Handlebars.js
*/
let themeHelpers = {
lazyLoadForContentImages: function(postText) {
let modifiedPostText = postText;
// Select all images from the content
modifiedPostText = modifiedPostText.replace(/<img[a-zA-Z0-9\s\"\'\=\-]*?src="(.*?)".*?>/gmi, function(match, url) {
// Create *-xs image path
let image = url.split('.');
if(
url.indexOf('/gallery/') === -1 && (
image[image.length - 1] === 'jpeg' ||
image[image.length - 1] === 'jpg' ||
image[image.length - 1] === 'png' ||
image[image.length - 1] === 'JPEG' ||
image[image.length - 1] === 'JPG' ||
image[image.length - 1] === 'PNG'
)
) {
let xsImage = image.slice(0, image.length - 1).join('.') + '-xs.' + image[image.length - 1];
xsImage = xsImage.split('/');
xsImage[xsImage.length - 2] = xsImage[xsImage.length - 2] + '/responsive';
xsImage = xsImage.join('/');
// Replace src attribute with *-xs image path
match = match.replace(/src=".*?"/gi, 'src="' + xsImage + '"');
// change srcset to data-srcset
match = match.replace('srcset="', 'data-srcset="');
// replace sizes with data-sizes
match = match.replace(/sizes=".*?"/i, 'data-sizes="auto"');
// add necessary CSS classes
if(match.indexOf('class="') > -1) {
match = match.replace('class="', 'class="lazyload blur-up ');
} else {
match = match.replace('<img', '<img class="lazyload blur-up"');
}
}
// return modified <img> tag
return match;
});
return modifiedPostText;
},
parseShortcode: function(postText, content) {
let allTags = content.data.website.contentStructure.tags;
let matches = postText.match(/\[posts tags="(.*?)" count="(.*?)"\]/gmi);
if(!matches) {
return postText;
}
for(let i = 0; i < matches.length; i++) {
let tags = matches[i].match(/tags="(.*?)"/);
let count = matches[i].match(/count="(.*?)"/);
count = count[1];
let tagNames = tags[1].split(',');
let posts = [];
let output = '';
for(let j = 0; j < tagNames.length; j++) {
for(let k = 0; k < allTags.length; k++) {
if(allTags[k].slug === tagNames[j]) {
for(let l = 0; l < allTags[k].posts.length; l++) {
posts.push(allTags[k].posts[l]);
}
}
}
}
posts = [...new Set(posts)];
posts.sort((a, b) => parseInt(a.createdAt) - parseInt(b.createdAt));
posts = posts.slice(0, count);
if(posts.length) {
output = '<ol>';
for(let j = 0; j < posts.length; j++) {
output += '<li><a href="' + posts[j].url + '">' + posts[j].title + '</a></li>';
}
output += '</ol>';
}
postText = postText.replace(matches[i], output);
}
return postText;
}
};
module.exports = themeHelpers;
Now we have to add the helper to the places where the content with shortcode should appear, so, for example, go to the post.habs file and edit it as follows:
Change the {{{text}}}
to {{{parseShortcode text}}}
Tips: You can find in our themes another helper {{{lazyLoadForContentImages text}}}
for loading the post image with lazy load, so we have to connect both of the helpers together. Thankfully, Handlebars offers support for subexpressions, which allows you to invoke multiple helpers within a single mustache. It's very easy to do:
{{{parseShortcode (lazyLoadForContentImages text)}}}
Finally open/create a post and add the following shortcode to it: [posts tags="x,y,z" count="5"] where x,y,z are the tag names separated by a comma and the 5 is the number of displayed tags.
An interesting thing to note here is that if the tags specified contain the same post, that post will only be displayed once on the list, rather than having any duplicates in the listing.