Reputation: 51
Assemble: 0.4.4
Grunt: 0.4.1
I'm designing a blog where I want to put 5 of the most recent posts on the front page. I've created a collection for my posts based on keywords:
assemble: {
options: {
flatten: false,
partials: '<%= build.src %>/_partials/*.hbs',
layoutdir: '<%= build.src %>/_layouts',
data: ['<%= build.src %>/_data/*.{json,yml}', 'package.json'],
assets: '<%= build.out %>/',
helpers: [ 'helper-moment','<%= build.src %>/helpers/helper-*.js'],
collections: [
{ name: 'keywords', inflection: 'keyword' }
]
},
YAML front matter on the various posts look similar to this:
--
layout: default.hbs
title: <%= site.title %>
description: "Adult Redeploy All Sites Summit 2015"
dateCreated: 06-23-2014
slug: "Welcome"
breadCrumbs: false
posted: 01-12-2014
keywords:
- news
navSort: 100
--
My code to display the titles and summaries is this:
<div>
{{#each keywords}}
{{#is keyword "news"}}
{{#withSort pages "data.posted" dir="desc"}}
<div>
<h2><a href="/{{relativeLink}}">{{data.title}}</a></h2>
<p>{{formatDate data.posted "%F"}}</p>
<div>
{{#markdown}}{{data.summary}}{{/markdown}}
</div>
<p><a href="/{{relativeLink}}">more...</a></p>
</div>
{{/withSort}}
{{/is}}
{{/each}}
</div>
This works. It displays all the blogs no problem. But I want to limit to 5 -- the five most recent.
I've looked at this issue:
https://github.com/assemble/assemble/issues/463
But I'm not sure how to incorporate it into the example above. Is there a way to limit the pages #withSort?
Confused.
Upvotes: 1
Views: 532
Reputation: 3374
Had the same problem and ended up creating a helper for it.
var _ = require('underscore');
var helpers = {
latest: function(array, amount, fn) {
var buffer = "";
_.chain(array)
.filter(function(i) {
return i.data.date;
})
.sortBy(function(i) {
return i.data.date;
})
.reverse()
.first(amount)
.forEach(function(i) {
buffer += fn.fn(i);
});
return buffer;
},
};
module.exports.register = function(Handlebars, options) {
options = options || {};
for (var helper in helpers) {
Handlebars.registerHelper(helper, helpers[helper]);
}
return this;
};
Add it somewhere along assemble.helpers search path. Make sure you have underscore dependency installed
npm install underscore --save-dev --save-exact
Then you can use the helper like this
<ul>
{{#latest pages 5}}
<li><a href="{{relativeLink}}">{{data.title}}</a></li>
{{/latest}}
</ul>
Upvotes: 1
Reputation: 51
Well, I finally got this working, but it's probably not the best way to go about it. In fact, it's brute-force all the way. But I'm going to post what finally worked.
Here's the setup -- slightly modified from my original question.
I wanted to create a partial -- sidebar.hbs -- that lists the 5 most recent posts. I then wanted to call that partial inside my "normal" pages when I wanted a sidebar with recent content. Okay, so here's the setup.
My assemble routine in grunt.js:
assemble: {
options: {
flatten: false,
partials: '<%= build.src %>/_partials/*.hbs',
layoutdir: '<%= build.src %>/_layouts',
data: ['<%= build.src %>/_data/*.{json,yml}', 'package.json'],
assets: '<%= build.out %>/',
helpers: [ 'helper-moment','<%= build.src %>/helpers/helper-*.js'],
collections: [
{ name: 'keywords',
inflection: 'keyword',
sortby: 'posted',
sortorder: 'desc',
}
]
},
I then realized -- much like you kindly responded above -- that I can loop through the collection:
{{#each keywords}}
{{#is keyword "news"}}
{{#withFirst pages 3}}
<h5 style="padding-left: 7px; padding: 0; margin: 0;">
<a href="{{relativeLink}}">{{data.slug}}</a></h5>
{{/withFirst}}
{{/is}}
{{/each}}
Except that -- and here's the rub: {{relativeLink}} is not properly set here. It returns nothing -- it's blank. I suspect I'm confusing contexts by calling the sidebar.hbs partial from a template page. I'm not sure, but I do know that the code below works as a partial -- and returns the proper {{relativeLink}}:
{{#each pages}}
{{data.slug}} -- {{relativeLink}}
<br/>
{{/each}}
The problem there, of course, is that it returns all the pages -- and they're not sorted.
So, in order to get my sidebar partial working -- and returning the proper links no matter where we are in the site hierarchy -- I created my links like this (and I'm embarrassed to post this -- but it works). I used the 'page.src' variable in the #withFirst loop. This works for what I want but seems awkward:
{{#each keywords}}
{{#is keyword "news"}}
{{#withFirst pages 4}}
<div class="myArticle">
<article class="recent-posts" >
<h5 style="padding-left: 7px; padding: 0; margin: 0;"><a href="{{data.siteFolder}}{{constructPostLinks src ext}}">{{data.slug}}</a></h5>
<p>
<span >
{{moment data.posted format ="DD MMM YYYY"}}
</span>
</p>
</article>
</div>
{{/withFirst}}
{{/is}}
{{/each}}
What I'm essentially do is calling a helper -- 'constructPostLinks' and hacking together a URL from the site's site's folder on my website (again, defined in data.yml), and the 'src' page variable (which I pass to my custom handlebars template). The 'src' page variable is not anything I need -- it usually looks like /src/about/index.hbs or something like that -- so I strip off the 'src' and replace the '.hbs' with 'ext' (in this case, '.html').
Here's the handlebars helper:
module.exports.register = function (Handlebars) {
Handlebars.registerHelper('constructPostLinks', function(page,ext) {
pageLink = page.replace('src/','').replace('.hbs',ext);
return new Handlebars.SafeString(pageLink);
});
};
I don't know. This seems like an awfully clumsy way to get the links generated, but it works. I can write a page -- index.hbs -- and then include the sidebar partial {{>sidebar}} -- and then everything is pulled together in the default page template (page.hbs) where the links are properly generated for the most recently posted articles. It works. But I wish the collection sort routine included the proper {{relativeLinks}}.
As I say, I'm sure I'm doing something wrong and confusing contexts -- but at least I've got it going.
Upvotes: 0
Reputation: 11931
It should be possible to accomplish your goal with a combination of the following:
{{#withFirst pages 5}}
helper to restrict the now-sorted list to your first five posts.Gruntfile
assemble: {
options: {
flatten: false,
partials: '<%= build.src %>/_partials/*.hbs',
layoutdir: '<%= build.src %>/_layouts',
data: ['<%= build.src %>/_data/*.{json,yml}', 'package.json'],
assets: '<%= build.out %>/',
helpers: [ 'helper-moment','<%= build.src %>/helpers/helper-*.js'],
collections: [
{ name: 'keywords', inflection: 'keyword', sortby: 'posted', sortorder: 'desc' }
]
},
Page Template
<div>
{{#each keywords}}
{{#is keyword "news"}}
{{#withFirst pages 5}}
<div>
<h2><a href="/{{relativeLink}}">{{data.title}}</a></h2>
<p>{{formatDate data.posted "%F"}}</p>
<div>
{{#markdown}}{{data.summary}}{{/markdown}}
</div>
<p><a href="/{{relativeLink}}">more...</a></p>
</div>
{{/withFirst}}
{{/is}}
{{/each}}
</div>
Upvotes: 0
Reputation: 11931
There's good news and bad news. The bad news is that I don't believe there is a built-in helper that both sorts and limits the pages collection, nor can you piece two of them together in a pipeline.
Edited: I was wrong, there may be a built-in way combining collection sorting and the withFirst helper. I'll make a separate answer.
The good news is that you can write your own custom Handlebars Helper to do this. I wrote a sample helper below based on the withSort Helper. You would use it like this:
<div>
{{#each keywords}}
{{#is keyword "news"}}
{{#withSortLimit pages sortBy="data.posted" dir="desc" limit=5}}
<div>
<h2><a href="/{{relativeLink}}">{{data.title}}</a></h2>
<p>{{formatDate data.posted "%F"}}</p>
<div>
{{#markdown}}{{data.summary}}{{/markdown}}
</div>
<p><a href="/{{relativeLink}}">more...</a></p>
</div>
{{/withSortLimit}}
{{/is}}
{{/each}}
</div>
withSortLimit.js Here is the no-frills source to the withSortLimit helper. You will need to register this in your Gruntfile's Assemble configuration as described in the options.helpers docs.
/*
* withSortLimit Handlebars Helper for Assemble
* Sample usage:
* {{#withSortLimit pages sortBy="data.posted" dir="desc" limit=5}}
* <li>{{formatDate data.posted "%F"}}: {{data.title}}</li>
* {{/withSortLimit}}
*/
(function() {
var _ = require("lodash");
function getPropertyFromSpec(obj, propertySpec) {
var properties = propertySpec.split('.');
var resultObj = obj;
_.each(properties, function (property) {
if (resultObj[property]) {
resultObj = resultObj[property];
}
});
return resultObj;
}
module.exports.register = function(Handlebars, options) {
Handlebars.registerHelper("withSortLimit", function(collection, options) {
var result = "";
var selectedPages = collection;
// Sorting
var sortProperty = options.hash.sortBy || "basename";
selectedPages = _.sortBy(collection, function (item) {
return getPropertyFromSpec(item, sortProperty);
});
if (options.hash.dir && options.hash.dir === "desc") {
selectedPages = selectedPages.reverse();
}
// Limit
if (options.hash.limit && options.hash.limit > 0) {
selectedPages = _.first(selectedPages, options.hash.limit);
}
// Rendering
_.each(selectedPages, function (page, index, pages) {
result += options.fn(page);
});
return result;
});
};
}).call(this);
Upvotes: 0