11ty / eleventy

A simpler site generator. Transforms a directory of templates (of varying types) into HTML.
https://www.11ty.dev/
MIT License
17.13k stars 493 forks source link

Handlebars reverse-iteration over collection #1097

Open ThePeach opened 4 years ago

ThePeach commented 4 years ago

Hello, I'm going through the pleasure/pain in converting my blog into 11ty. The stack I was on before was Metalsmith and Handlebars, and as you can imagine I'm trying to avoid rewriting the templates, leaving them in Handlebars (and also because compact and logic-less).

I've found extremely useful the eleventy-base-blog and I've taken from there the way tags pages (i.e. a list of posts filed under a specific tag, without pagination) are generated (see more at tags.njk.

My tags.md file is:

---
pagination:
  data: collections
  size: 1
  alias: tag
  filter:
    - all
    - posts
    - newestPosts
renderData:
  title: "{{ tag }} articles"
permalink: "/tags/{{ tag | slug }}/"
layout: posts-list.hbs
---

And the posts-list.hbs template is:

---
layout: base.hbs
---
<main id="main">
  <!-- list of articles -->
  <header class="main__header">
    <h2 class="title-fancy" tabindex="0">
      {{ tag }} articles
    </h2>
  </header>

  <ul class="listing">
    {{#each (lookup collections tag) }}
    <li>{{> partials/article-excerpt }}</li>
    {{/each}}
  </ul>
</main>

Now the problem is that I cannot seem to find a decent method to reverse-iterate the specific tag collection, while this is trivial when creating a pagination by using the reverse: true option.

Any idea? Or should I give up in this specific instance, either by starting to use Nunjucks, or by creating a Handlebar filter (I guess)?

carlos-ds commented 4 years ago

I also can't see any easier solution than creating a helper/filter to your eleventy config. When I add a helper like this:

module.exports = function (eleventyConfig) {
  eleventyConfig.addHandlebarsHelper("reverse", function (arr) {
    arr.reverse();
  });

  // ...
};

Then we can implement the reverse functionality as:

---
test: ["1", "2", "3", "4", "5"]
---
<ul>
  {{reverse test}}
  {{#each test }}
      <li>{{this}}</li>
  {{/each}}
</ul>

This will return the list as 5, 4, 3, 2, 1.

pdehaan commented 4 years ago

Possibly this, if you're using Nunjucks: https://mozilla.github.io/nunjucks/templating.html#reverse

{% for i in [1, 2, 3, 4] | reverse %}
    {{ i }}
{% endfor %}

Although it might need a bit of testing since I'm not sure what versions of Nunjucks that is supported in, and Eleventy might not be using the latest version of Nunjucks library. But I suppose you could also manually install Nunjucks and polyfill it in, per https://www.11ty.dev/docs/languages/nunjucks/#nunjucks-options.

Oh, sorry, I think I saw .njk above in your comment and missed the part where you said you were using .hbs.

ThePeach commented 4 years ago

@carlos-ds thank you so much to confirm my doubts @pdehaan no worries, I'd be very much keen in stripping out any type of logic from the template, and instead have the page control how the ordering should be done, this will allow for much greater reusability of template partials, regardless them being hbs or njk.

The problem with custom ordering of dynamic collections is possibly the biggest showstopper for me to publish my sites using 11ty :(

[edit] I'm also wondering if it's possible to add an ordered list variable within the same collection to handle this situation, because without the 11ty supporting this natively, I can't see how this can be solved without hacking it.