What the the scope of Liquid variables on Jekyll o

2019-07-17 08:57发布

问题:

I have created a simple Jekyll page (processed and hosted by GitHub Pages) with a Liquid counter variable where I loop through some data and dump it out and count the number of items with a given property.

For example:

Complete Before: {{ complete }}
{% for book in books %}
Title: {{book.Title}}
{% if book.Completed == "true" %}
{% increment completed %}
{% endif %}
{% endfor %}
Complete After: {{ complete }}

Now I have the same chunk of code on two different pages, but the data in books is different it both cases, but the value of the variable completed looks like it's being saved across the processing of both pages which results in page one looking like

Complete Before:
Title: Foo
Title: Bar
Complete After: 2

and page two looks like

Complete Before: 2
Title: Baz
Complete After: 3

How can I get the completed variable to be unique per page without needing to change each page to use a different variable name.

回答1:

I recommend to use assign in this case and increment the variable with the plus filter. increment behaves a little different than one expects, it does not modify a common Jekyll variable value:

{% assign complete=0 %}
Complete Before: {{ complete }}
 {% for book in books %}
 Title: {{book.Title}}
 {% if book.Completed == "true" %}
 {% complete=complete | plus:'1'  %}
 {% endif %}
 {% endfor %}
 Complete After: {{ complete }}

Update

The problem relies in its scope, at least, that is not what I naturally expect, this is a simple example to show it:

{% for post in site.posts limit: 2%}
increment {{forloop.index}}
- before {{complete}}
- current increment: {% increment complete %}
- after {{complete}}

<hr>
{% endfor %}

New loop

{% for post in site.posts limit: 2%}
increment {{forloop.index}}
- before {{complete}}
- current increment: {% increment complete %}
- after {{complete}} EXPECTED: {{forloop.index}}

<hr>
{% endfor %}

Output:

increment 1

    before
    current increment: 0
    after 1

increment 2

    before 1
    current increment: 1
    after 2

New loop

increment 1

    before 2
    current increment: 2
    after 3 EXPECTED: 1

increment 2

    before 3
    current increment: 3
    after 4 EXPECTED: 2


回答2:

If you look at liquid::increment code, you can see how it works and where is the boundary between context myvar and local myvar.

note that context myvar is passed across all pages, posts. It's a "global" variable.

On first page :

{% increment myvar %} -> output : 0

  • retrieve context myvar, assign 0 if it's nil
  • store it in a temporary variable
  • does a post increment (myvar++)
  • stores the value (1) in the myvar context variable
  • print temporary variable (0)

{{ myvar }} -> output : 1

As myvar is not assigned to a local variable, this liquid tag is using the fallback value of the context variable myvar but doesn't modify it.

On second page :

{% assign myvar = 100 %}

assign 100 to local myvar variable.

{% increment myvar %} -> output : 1

  • retrieve context myvar, which is 1
  • store it in a temporary variable
  • does a post increment (myvar++)
  • stores the value (2) in the myvar context variable
  • print temporary variable (1)

{{ myvar }} -> output : 100

As myvar is assigned to a local variable, use it.

Conclusion

If you need a local counter use @marcanuy solution.

If you need a global counter use {% increment myvar %} tag and do not rely on {{ myvar }} to print its value, because you're not sure that a local myvar is assigned/declared.