Jekyll/Liquid模板:如何按年份对博客文章进行分组?

56

我正在重写我的博客以使用Jekyll。 Jekyll使用Liquid模板语言,使得定制变得更加困难。

我想按年份对我的博客文章列表进行分组。如何编写Liquid代码来实现此功能?

{% for post in site.posts %}
  <li><!-- display post year here (but only once, per year) --></li>
  <li>
    <a href="{{ post.url }}">{{ post.title }}</a>
  </li>
{% endfor %}

1
它涉及或涉及捕获,真的很可怕。http://mikerowecode.com/2010/08/jekyll_archives_grouped_by_year.html - Ry-
8个回答

66

使用的Liquid代码量可以比现有答案中要少得多:

{% for post in site.posts %}
  {% assign currentdate = post.date | date: "%Y" %}
  {% if currentdate != date %}
    <li id="y{{currentdate}}">{{ currentdate }}</li>
    {% assign date = currentdate %} 
  {% endif %}
    <li><a href="{{ post.url }}">{{ post.title }}</a></li>
{% endfor %}

这将精确返回您在问题中指定的HTML:

<li id="y2013">2013</li>
<li><a href="/2013/01/01/foo/">foo</a></li>
<li id="y2012">2012</li>
<li><a href="/2012/02/01/bar/">bar</a></li>
<li><a href="/2012/01/01/baz/">baz</a></li>
然而,这并不是最佳解决方案,因为年份只是一个列表项。
将年份放在标题中,并开始一个新的<ul>来显示每年的文章并不需要太多的 Liquid 代码:
{% for post in site.posts %}
  {% assign currentdate = post.date | date: "%Y" %}
  {% if currentdate != date %}
    {% unless forloop.first %}</ul>{% endunless %}
    <h1 id="y{{post.date | date: "%Y"}}">{{ currentdate }}</h1>
    <ul>
    {% assign date = currentdate %}
  {% endif %}
    <li><a href="{{ post.url }}">{{ post.title }}</a></li>
  {% if forloop.last %}</ul>{% endif %}
{% endfor %}

生成的HTML:

<h1 id="y2013">2013</h1>
<ul>
<li><a href="/2013/01/01/foo/">foo</a></li>
</ul>
<h1 id="y2012">2012</h1>
<ul>
<li><a href="/2012/02/01/bar/">bar</a></li>
<li><a href="/2012/01/01/baz/">baz</a></li>
</ul>
你也可以按月份和年份分组,这样标题将会是“2012年2月”,“2012年1月”等。
要实现这个功能,你只需要将date: "%Y"(在上述两个示例的第二行)替换为date: "%B %Y"即可。
%B 是全名月份,请参见文档。)

这太棒了,谢谢!一个注意点是你没有为最后一组创建最终的</ul>。所以,在for循环之后,你需要做一些像{% if site.posts.size != 0 %}</ul>{% endif %}这样的事情。 - spitzanator
1
或者在循环结束之前,{% if forloop.last %}</ul>{% endif %}。 - lfk
我想补充一下,其他的解决方案对我没有用,因为我的帖子不一定是按日期排序的,但这个解决方案有效!谢谢! - counterbeing
这是一篇很棒的文章。我成功地将其适应到了我的情况中。但我对它的工作原理感到困惑 - 有人能否在Liquid代码中添加注释来更新这个答案? - joshreesjones
除了年份和月份,是否可以显示帖子数量? - Robur_131

40

有没有可能使用site.data而不是posts来完成这个任务?例如,我有10组数据,每组数据都有一个日期变量,这些日期分为3个时间段,我想像这样循环。或者只能使用嵌套的Liquid循环吗?谢谢! - Rhys
是的,您可以使用相同的技术对任何数组进行分组:{% assign groupedData = site.data | group_by_exp: "data", "data.yourVariable" %} - Trevor
1
感谢你的出色回答。如果你在使用较新版本的 Jekyll,那么这绝对是正确的方法。 - coryetzkorn
感谢您的回答。文档可以在此处找到:https://jekyllrb.com/docs/liquid/filters/#group-by-expression - yo1995

36

如果您想按年份拆分,这是代码:

{% for post in site.posts  %}
    {% capture this_year %}{{ post.date | date: "%Y" }}{% endcapture %}
    {% capture next_year %}{{ post.previous.date | date: "%Y" }}{% endcapture %}

    {% if forloop.first %}
    <h2 id="{{ this_year }}-ref">{{this_year}}</h2>
    <ul>
    {% endif %}

    <li><a href="{{ post.url }}">{{ post.title }}</a></li>

    {% if forloop.last %}
    </ul>
    {% else %}
        {% if this_year != next_year %}
        </ul>
        <h2 id="{{ next_year }}-ref">{{next_year}}</h2>
        <ul>
        {% endif %}
    {% endif %}
{% endfor %}

如果你想将其分解为年份和月份,可以按如下方式实现:

{% for post in site.posts  %}
    {% capture this_year %}{{ post.date | date: "%Y" }}{% endcapture %}
    {% capture this_month %}{{ post.date | date: "%B" }}{% endcapture %}
    {% capture next_year %}{{ post.previous.date | date: "%Y" }}{% endcapture %}
    {% capture next_month %}{{ post.previous.date | date: "%B" }}{% endcapture %}

    {% if forloop.first %}
    <h2 id="{{ this_year }}-ref">{{this_year}}</h2>
    <h3 id="{{ this_year }}-{{ this_month }}-ref">{{ this_month }}</h3>
    <ul>
    {% endif %}

    <li><a href="{{ post.url }}">{{ post.title }}</a></li>

    {% if forloop.last %}
    </ul>
    {% else %}
        {% if this_year != next_year %}
        </ul>
        <h2 id="{{ next_year }}-ref">{{next_year}}</h2>
        <h3 id="{{ next_year }}-{{ next_month }}-ref">{{ next_month }}</h3>
        <ul>
        {% else %}    
            {% if this_month != next_month %}
            </ul>
            <h3 id="{{ this_year }}-{{ next_month }}-ref">{{ next_month }}</h3>
            <ul>
            {% endif %}
        {% endif %}
    {% endif %}
{% endfor %}

关键只在于你在循环中做出了何种抉择。


这个能够与 site.categories.xxx 一起工作吗? - arunkumar

10

上面的一些解决方案非常复杂,但正如@Trevor所指出的,我们可以利用Jekyll的group_by_exp过滤器。我喜欢这个解决方案,但我需要按年分组,然后在该列表内按月分组。因此,我稍微修改了它。

{% assign postsByYear = site.posts | group_by_exp:"post", "post.date | date: '%Y'" %}
    {% for year in postsByYear %}
      <h1>{{ year.name }}</h1>
      {% assign postsByMonth = year.items | group_by_exp:"post", "post.date | date: '%B'" %}

      {% for month in postsByMonth %}
        <h2>{{ month.name }}</h2>
        <ul>
          {% for post in month.items %}
            <li><a href="{{ post.url }}">{{ post.title }}-{{ post.date }}</a></li>
          {% endfor %}
        </ul>

      {% endfor %}
    {% endfor %}

6

这是 Ankit R Gadiya 的回答的改编版。内部的 for 循环在显示 HTML 代码时需要去除缩进才能使其正确呈现标记语言。我还添加了帖子的摘录:

{% assign postsByYear = site.posts | group_by_exp:"post", "post.date | date: '%Y'" %}
{% for year in postsByYear %}
  <h1>{{ year.name }}</h1>
  {% assign postsByMonth = year.items | group_by_exp:"post", "post.date | date: '%B'" %}

{% for month in postsByMonth %}
<h2>{{ month.name }}</h2>
<ul>
  {% for post in month.items %}
    <li>
      <a href="{{ post.url }}">{{ post.title }}</a>
      <br>{{ post.excerpt }}
    </li>
  {% endfor %}
</ul>

{% endfor %}
{% endfor %}

示例:

示例


3
<ul>
  {% for post in site.posts %}
      {% assign year = post.date | date: "%Y" %}

      {% if year != prev_year %}
        <h3>{{year}}</h3>
      {% endif %}

      <li>
        <span>{{ post.date | date: "%B %e, %Y" }}</span>
        <a href="{{ post.url }}">{{ post.title }}</a>
      </li>
      {% assign prev_year = year %}
  {% endfor %}
</ul>

2
我不太喜欢其他答案,所以这里有一个替代方案。基本逻辑:仅在“新的”情况下显示年/月: "最初的回答"
{% assign var currentYear = 0 %}
{% assign var currentMonth = 0 %}
{% for post in site.posts  %}
{% capture year %}{{ post.date | date: "%Y" }}{% endcapture %}
{% capture month %}{{ post.date | date: "%B" }}{% endcapture %}

{% if currentYear != year %}
<div>
  <h2>{{ year }}</h2>
</div>
{% assign var currentYear = year %}
{% endif %}
{% if currentMonth != month %}
<div>
  <h3>{{ month }}</h3>
</div>
{% assign var currentMonth = month %}
{% endif %}
<p>{{ post.title }}</p>
{% endfor %}

2

尝试:

{% for post in site.posts  %}
  {% capture this_year %}{{ post.date | date: "%Y" }}{% endcapture %}

  {% if forloop.first %}
  <h2 id="{{ this_year }}-ref">{{this_year}}</h2>
  <ul class="posts">
  {% else %}
      {% if this_year != last_year %}
      </ul>
      <h2 id="{{ this_year }}-ref">{{this_year}}</h2>
      <ul class="posts">
      {% endif %}
  {% endif %}

    <li>
      <span class="post-date">{{ post.date | date_to_string }} &raquo;</span>
      <a href="{{ post.url }}">{{ post.title }}</a>
    </li>

  {% if forloop.last %}
    </ul>
  {% endif %}

  {% capture last_year %}{{ this_year }}{% endcapture %}
{% endfor %}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接