Ruby的Liquid模板引擎中的模数(或其缺乏)

23

我正在制作一个Jekyll网站,想要输出嵌套在行div中的三列div。使用Liquidcycle过滤器可以很容易地实现:

{% for p in site.categories.post %}
    {% cycle 'add rows': '<div class="row">', nil, nil %}
        <div class="column">
            <a href="{{ p.url }}">{{ p.title }}</a>
        </div>
    {% cycle 'close rows': nil, nil, '</div>' %}
{% endfor %}
然而,这种方法仅适用于有3、6、9等帖子的情况。当帖子总数不是3的倍数时,<div class="row"> 标签无法在 close rows 循环中得到闭合 - 在关闭标签之前,for循环就已经结束了。
在Ruby、PHP或其他语言中,我可以很容易地通过模运算符改正这个问题,因此除了 close rows 循环之外,当 if site.categories.size % 3 == 0 时,我还会输出 </div> 。然而,由于Liquid是一种安全的模板语言,它不支持模运算符。
如果帖子总数不是3的倍数,还有什么其他方法可以正确地关闭 <div class="row"> 标签呢?
7个回答

15

我发现这种方法非常有效!

{% assign mod = forloop.index0 | modulo:4 %}
{% if mod == 0 %}
   <!-- Do stuff -->
{% endif %}

取模运算符可能是在较新版本的Jekyll中添加的。但它在我的项目中绝对有效。 - ThisClark

13

对于你的具体例子,你可以在 {% endfor %} 之后使用 {% cycle 'close rows': nil, '</div>', '</div>' %}


8

目前唯一的方法是编写一个Liquid过滤器来完成这个任务。在适当的代码位置注册过滤器(如果与Rails一起使用和不使用时位于不同位置)。

Liquid::Template.register_filter(LiquidFilters)

在你的项目/lib目录中添加liquid_filters.rb文件:

module LiquidFilters  
  # makes modulus operation available to templates
  def mod(data, param)
    data % param
  end  
end

接下来,您可以在模板中按照以下方式使用它: {{ variable | mod:5 }}

如果您需要将其用于某些逻辑,请捕获该值。

{% capture modulus %}{{ variable | mod:5 }}{% endcapture %}

我注意到捕获的值是一个字符串,因此为了进行比较,您需要使用。
{% if modulus == "0" %}
 ..
{% endif %}

3

我知道这个问题已经被解决了,但最近我在Liquid中遇到了这种情况,所以我想提供我的解决方案,以防有人有类似的标记要求。

在我的情况下,我已经通过一个if语句验证至少有一篇文章,所以我在循环之外创建了第一个“row” div。我在for循环后也关闭了它。这可以防止出现少于三篇文章的情况。

<div class="row">

    {% for p in posts %}
        <div class="column">
            <!-- Post code here -->
        </div>
        {% unless forloop.last %}
            {% cycle '', '', '</div><div class="row">' %}
        {% endunless %}
    {% endfor %}

</div>

在每三篇文章之后,cycle会关闭当前行并打开新的一行,除非该文章是forloop中的最后一篇文章,在这种情况下,我们不想打开新的一行,让包装的</div>将其关闭。

3
我在for循环中使用了另一个技巧:在您的情况下没用,但是如果您只想要一个模数,以查找行是否已结束并且需要新行(就像我一样),则是有用的。
在这个例子中,我将使用一个由4个项目组成的行:
{% assign currentRow = 1 %}
# enter the for loop ... then, with 4 as the divisor:
{% if forloop.index == 4 * currentRow %}
  # do whatever you want
  {% assign currentRow = currentRow + 1 %}
{% endif %}
# exit the for loop

虽不太友善,但易于操作。


2
我从这篇文章中学到了很多,其中有三种模式我在项目中使用过。它也很适配Bootstrap。只需在以下代码中更改列类别即可。相同的模式也可以应用于其他需要模数的情况,例如奇偶行。希望对某些人有所帮助 - 四列:
<div class="container">
    {% for post in site.posts %}
        {% cycle 'add row' : '<div class="row">', nil, nil, nil %}
            <div class="column">
                <!-- liquid tags here -->
            </div>
        {% cycle 'end row' : nil, nil, nil, '</div>' %}
    {% endfor %}
    {% cycle 'end row' : nil, '</div>', '</div>', '</div>' %}
</div>

三列布局:

<div class="container">
    {% for post in site.posts %}
        {% cycle 'add row' : '<div class="row">', nil, nil %}
            <div class="column">
                <!-- liquid tags here -->
            </div>
        {% cycle 'end row' : nil, nil, '</div>' %}
    {% endfor %}
    {% cycle 'end row' : nil, '</div>', '</div>' %}
</div>

双列布局:

<div class="container">
    {% for post in site.posts %}
        {% cycle 'add row' : '<div class="row">', nil %}
            <div class="column">
                <!-- liquid tags here -->
            </div>
        {% cycle 'end row' : nil, '</div>' %}
    {% endfor %}
    {% cycle 'end row' : nil, '</div>' %}
</div>

0

据我所知,Liquid不会阻止模运算,只是阻止了%字符。您可以在不使用%运算符的情况下执行模运算。例如,14.modulo(3) => 2而不是14 % 3


这是有道理的,因为所有其他算术函数都是这样抽象出来的,但不幸的是,modulomodulus都不起作用... - Andrew
是的。我刚检查了源代码。可惜没有“模运算”。也许我得分叉它并加入进去。我的当前解决方案很混乱:“除非总数等于3或6或9或12或15...” - Andrew
1
x.modulo(y) 只是 x.divmod(y)[1] 的一个别名。如果允许使用 divmod,您可以使用该形式。或者,您也可以自己编写模运算函数:x - (x / y)(使用您需要的任何抽象版本的算术运算符)。 - bta

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