Liquid and Arithmetic - pagination

I am working on some pagination and I am wondering if there is a way to tell liquid to only show 5 pages. The output I am looking for is
<< First 5 6 7 8 9 Last >>
The logic I currently have in place works but it is showing all 30 some pages.
{% for count in (2..paginator.total_pages) %}
{% if count == paginator.page %}
<span class="current">{{ count }}</span>
{% else %}
{{ count }}
{% endif %}
{% endfor %}
I would like to be able to make the 2 and paginator.total_pages be dynamic, I have tried
{% for count in ((paginator.page - 2)..(paginator.page + 2)) %}
This code however does not actually do the math, if paginator.page = 5 then the loop is 5..5 and does not provide the expected results. I can figure out the logic so that it does not hit negative numbers and works as expected but how can I do math equations in this?

You need use a filter on paginator.total_pages to do the arithmetic, and then capture the result in a variable using the capture tag. Once you have the start and end pages, you can write the for loop as you normally would:
{% capture page_start %}{{ paginator.page | minus: 2 }}{% endcapture %}
{% capture page_end %}{{ paginator.page | plus: 2 }}{% endcapture %}
{% for count in (page_start..page_end) %}
{% comment %} ... do your thing ... {% endcomment %}
{% endfor %}

I'm building a blog with Jekyll and I've faced a similar situation. According to what I've found in the Liquid wiki is possible to iterate over a subset of a given collection using limit and offset.
The following example reflects your particular case and should work correctly in every page, from the first to the last one:
{% capture start %}{{ paginator.page | minus: 3 }}{% endcapture %}
{% for i in (1..paginator.total_pages) limit: 5 offset: start %}
...
{% endfor %}

I am using Bootstrap 3.0.3 for my website. I use the following code for pagination.
It has the same effect you're looking for. The code I posted above is what you're looking for, but I will post my bootstrap pagination code here anyways.
{% if paginator.total_pages != 1 %}
{% if paginator.total_pages < 7 %}
<div class="page-body col-md-12">
<ul class="pagination pagination-centered">
{% if paginator.total_pages >= 10 %}
{% if paginator.previous_page %}
<li>
««
</li>
{% else %}
<li class="disabled">
<a>««</a>
</li>
{% endif %}
{% endif %}
{% if paginator.previous_page %}
{% if paginator.previous_page == 1 %}
<li>
«
</li>
{% else %}
<li>
«
</li>
{% endif %}
{% else %}
<li class="disabled">
<a>«</a>
</li>
{% endif %}
{% if paginator.page == 1 %}
<li class="active">
<a>1</a>
</li>
{% else %}
<li>
1
</li>
{% endif %}
{% for count in (2..paginator.total_pages) %}
{% if count == paginator.page %}
<li class="active">
<a>{{count}}</a>
</li>
{% else %}
<li>
{{count}}
</li>
{% endif %}
{% endfor %}
{% if paginator.next_page %}
<li>
»
</li>
{% else %}
<li class="disabled">
<a>»</a>
</li>
{% endif %}
{% if paginator.total_pages >= 10 %}
{% if paginator.next_page %}
<li>
»»
</li>
{% else %}
<li class="disabled">
<a>»»</a>
</li>
{% endif %}
{% endif %}
</ul>
</div>
{% else %}
{% assign page_start = paginator.page | minus: 2 %}
{% assign page_end = paginator.page | plus: 2 %}
{% if page_end > paginator.total_pages %}
{% assign page_end = paginator.total_pages %}
{% assign page_start = paginator.page | minus: 4 %}
{% endif %}
{% if page_start < 2 %}
{% assign page_end = paginator.page | plus: 3 %}
{% assign page_start = paginator.page | minus: 1 %}
{% endif %}
{% if page_start == 0 %}
{% assign page_end = paginator.page | plus: 4 %}
{% assign page_start = paginator.page %}
{% endif %}
<div class="page-body col-md-12">
<ul class="pagination pagination-centered">
{% if paginator.total_pages > 5 %}
{% if paginator.previous_page %}
<li>
««
</li>
{% else %}
<li class="disabled">
<a>««</a>
</li>
{% endif %}
{% endif %}
{% if paginator.previous_page %}
{% if paginator.previous_page == 1 %}
<li>
«
</li>
{% else %}
<li>
«
</li>
{% endif %}
{% else %}
<li class="disabled">
«
</li>
{% endif %}
{% if page_start == 1 %}
{% assign page_end = paginator.page | plus: 4 %}
{% assign page_start = 2 %}
{% if paginator.page == 1 %}
<li class="active">
1
</li>
{% else %}
<li>
1
</li>
{% endif %}
{% endif %}
{% for count in (page_start..page_end) %}
{% if count == paginator.page %}
<li class="active">
{{count}}
</li>
{% else %}
<li>
{{count}}
</li>
{% endif %}
{% endfor %}
{% if paginator.next_page %}
<li>
»
</li>
{% else %}
<li class="disabled">
»
</li>
{% endif %}
{% if paginator.total_pages > 5 %}
{% if paginator.next_page %}
<li>
»»
</li>
{% else %}
<li class="disabled">
<a>»»</a>
</li>
{% endif %}
{% endif %}
</ul>
</div>
{% endif %}
{% endif %}

Try this instead
{% if paginator.total_pages < 7 %}
{% if paginator.page == 1 %}
<span class="current bold">1</span>
{% else %}
1
{% endif %}
{% for count in (2..paginator.total_pages) %}
{% if count == paginator.page %}
<span class="current bold">{{ count }}</span>
{% else %}
{{ count }}
{% endif %}
{% endfor %}
{% else %}
{% assign page_start = paginator.page | minus: 2 %}
{% assign page_end = paginator.page | plus: 2 %}
{% if page_end > paginator.total_pages %}
{% assign page_end = paginator.total_pages %}
{% assign page_start = paginator.page | minus: 4 %}
{% endif %}
{% if page_start < 2 %}
{% assign page_end = paginator.page | plus: 3 %}
{% assign page_start = paginator.page | minus: 1 %}
{% endif %}
{% if page_start == 0 %}
{% assign page_end = paginator.page | plus: 4 %}
{% assign page_start = paginator.page %}
{% endif %}
{% if page_start == 1 %}
{% assign page_end = paginator.page | plus: 4 %}
{% assign page_start = 2 %}
{% if paginator.page == 1 %}
<span class="current bold">1</span>
{% else %}
1
{% endif %}
{% endif %}
{% for count in (page_start..page_end) %}
{% if count == paginator.page %}
<span class="current bold">{{ count }}</span>
{% else %}
{{ count }}
{% endif %}
{% endfor %}
{% endif %}

Related

Shopify file_url isn't showing correctly

I'm editing the following
{% for option in product.options %}
{% if option == 'Colour' %}
{% assign index = forloop.index0 %}
{% assign colorlist = '' %}
{% assign color = '' %}
{% for variant in product.variants %}
{% capture color %}{{ variant.options[index] }}{% endcapture %}
{% unless colorlist contains color %}
<button class="product-colour__item">
<label class="product-colour__item-label" style="background-image:url("{{ color | downcase | handleize | append: '.png' | file_url }}");">
</label>
</button>
{% capture tempList %}{{colorlist | append: color | append:''}}{% endcapture %}
{% assign colorlist = tempList %}
{% endunless %}
{% endfor %}
{% endif %}
{% endfor %}
and it's meant to show like:
style="background-image:url("//cdn.shopify.com/s/files/1/0584/2419/7328/products/deep-rose.png");
instead its showing like:
style="background-image:url(" cdn.shopify.com s files 1 0584 2419 7328 deep-rose.png");
I know it's missing the /products/ as well as the rest of it.
What am I doing wrong?

How to manipulate count based on sub-values in TWIG?

I was wondering how to manipulate / render the count based on sub-rules.
In the current situation specifications are counted and when there are more than 5, a link is shown to show more specifications. I wanted to show only specs that have a value like this:
{% for spec in product.specs | limit(5) %}
{% if spec.value %}
<li>
<span>{{ spec.title }}</span>
{{ spec.value }}
</li>
{% endif %}
{% endfor %}
{% if product.specs | length > 5 %}
<li class="more">{{ 'View all specifications' | t }}</li>
{% endif %}
In this case the count is done before checking the values of the sub-items, so the "Show more" link is visible and clickable, but doesn't show more specifications, because they are stripped out, because the value is empty but is counted as item.
The goal is to hide the "Show more" link when there are < 5 items WITH values.
I hope anyone could point me in the right direction :-)
Thank you very much for thinking with me!
You could either use an extra counter to count the valid elements,
{% for spec in product.specs | limit(5) %}
{% if spec.value %}
<li>
<span>{{ spec.title }}</span>
{{ spec.value }}
</li>
{% endif %}
{% endfor %}
{% set cnt = 0 %}
{% for spec in product.specs %}
{% if spec.value %}{% set cnt = cnt + 1 %}{% endif %}
{% endfor %}
{% if cnt >= 5 %}
<li class="more">{{ 'View all specifications' }}</li>
{% endif %}
Or you can use the filter filter
{% if products.specs| filter(spec => spec.value|default) | length >= 5 %}
<li class="more">{{ 'View all specifications' }}</li>
{% endif %}
Update as for your comment (and as you don't have access to filter)
You can't just limit the result before hand. So in the first part you would also need to use a counter
{% set cnt = 0 %}
{% for spec in product.specs %}
{% if cnt < 5 %}
{% if spec.value %}
<li>
<span>{{ spec.title }}</span>
{{ spec.value }}
</li>
{% set cnt = cnt + 1 %}
{% endif %}
{% endif %}
{% endfor %}

How to chop inside a for-loop in Twig

This is a refactoring question. The code works as is, I'm just not happy with it in an aesthetical sense.
I would like to know if the conditional inside the loop can be written in a shorter, more readable way or maybe can be stripped away?
{% set i = 0 %}
{% for element in list %}
{% if loop.first %}<div class="row">{% endif %} {# open first row #}
{% if i > 2 %} {# new row every 3 elements #}
{% set i = 0 %}
</div>
<div class="row">
<img src="{{ element.url }}">
{% else %}
{% set i = i+1 %}
<img src="{{ element.url }}">
{% endif %}
{% if loop.last %}</div>{% endif %}
{% endfor %}
As user DarkBee said, have a look into batch.
{% for element in list|batch(3) %}
.....
.....
{% endfor %}
Just to have an example on this page.
Batch-Docs
Regards

Access posts from other page in jekyll pagination

Jekyll pagination enables you to arrange posts neatly on pages. I would like to put the range of dates as a tooltip on the links to the pages, just as:
<li class="pager-item">
Page 2
</li>
The obvious way to do so would be to take the date of the first and the last post on a page. Unfortunatly jekyll-pagination only seems to deliver a list of posts for the current page via paginator.posts.
Maybe I just can't find it in the documentation, so I ask you: is it possible to access a list of posts from another page in jekyll pagination? Something like paginator.pages.2.posts?
An alternate approach would be to build my own paginaton, which should be no problem, but I would prefer to use built-in functions.
You can get the information indirectly:
paginator.previous_page and paginator.next_page give you the page number of respectively the previous and the next pagination page, or nil if the page does not exists.
paginator.per_page gives you the number of posts per page.
paginator.total_posts gives you the total number of posts in the site.
site.posts gives you a reverse chronological list of all posts.
So given the current paginator object, ensuring paginator.next_page != nil, you know the posts from the next pagination page are indexed in site.posts from a to b (included) with:
a = (paginator.next_page) * paginator.per_page
b = a + paginator.per_page - 1
Last pagination page may not be complete: remember checking b < paginator.total_posts.
Ensuring paginator.previous_page != nil, same goes for previous pagination page with:
a = (paginator.previous_page) * paginator.per_page
b = a + paginator.per_page - 1
My current approach looks like this:
{% if include.page > 0 and include.page <= paginator.total_pages %}
{% assign first = include.page | minus:1 | times:paginator.per_page %}
{% assign last = paginator.per_page | times:include.page | minus:1 %}
{% if last > paginator.total_posts %}
{% assign last = paginator.total_posts | minus:1 %}
{% endif %}
{{ site.posts[first].date | date: "%d.%m.%Y" }} bis {{ site.posts[last].date | date: "%d.%m.%Y" }}
{% else %}
OUT_OF_RANGE
{% endif %}
I really don't like the Liquid-Syntax :D
Or :
{% comment %} NOTE : shortcut "p" for "paginator" {% endcomment %}
{% assign p = paginator %}
{% comment %} date formating (see shorthand formats in Ruby's documentation : http://ruby-doc.org/core-2.3.3/Time.html#method-i-strftime) {% endcomment %}
{% assign dateFormat = "%y %b %d" %}
<ul>
{% for post in p.posts %}<li>{{ post.title }} - {{ post.date | date: dateFormat }}</li>
{% endfor %}
</ul>
{% comment %}++++++++++ if previous page == newest posts {% endcomment %}
{% if paginator.previous_page %}
{% assign prevPage = p.page | minus: 1 %}
{% assign prevPageLastIndex = prevPage | times: p.per_page | minus: 1 %}
{% assign prevPageFirstIndex = prevPageLastIndex | minus: p.per_page | plus: 1 %}
{% assign prevPagefirstPostDate = site.posts[prevPageFirstIndex].date | date: dateFormat %}
{% assign prevPagenextPageLastPostDate = site.posts[prevPageLastIndex].date | date: dateFormat %}
{% capture prevPageLink %}
<p><a href="{{ paginator.previous_page_path }}"></p>
Newest posts from
{% if prevPagefirstPostDate != prevPagenextPageLastPostDate %}
{{ prevPagenextPageLastPostDate }} to {{ prevPagefirstPostDate }}
{% else %}
{{ prevPagenextPageLastPostDate }}
{% endif %}
</a>
{% endcapture %}
{% endif %}
{% comment %} ++++++++++ if next page = oldest posts {% endcomment %}
{% if paginator.next_page %}
{% assign nextPage = p.page | plus: 1 %}
{% assign nextPageFirstIndex = p.page | times: p.per_page %}
{% comment %}>>Next page is not the last page = normal computing {% endcomment %}
{% if nextPage != p.total_pages %}
{% assign nextPageLastIndex = nextPageFirstIndex | plus: p.per_page | minus: 1 %}
{% comment %}>>Next page is the last page compute index from p.total_posts{% endcomment %}
{% else %}
{% assign nextPageLastIndex = p.total_posts | minus: 1 %}
{% endif %}
{% assign nextPagefirstPostDate = site.posts[nextPageFirstIndex].date | date: dateFormat %}
{% assign nextPageLastPostDate = site.posts[nextPageLastIndex].date | date: dateFormat %}
{% capture nextPageLink %}
<p><a href="{{ paginator.next_page_path }}"></p>
Previous posts from
{% if nextPagefirstPostDate != nextPageLastPostDate %}
{{ nextPageLastPostDate }} to {{ nextPagefirstPostDate }}
{% else %}
{{ nextPageLastPostDate }}
{% endif %}
</a>
{% endcapture %}
{% endif %}
{{ prevPageLink }}
{{ nextPageLink }}

A solution for grouping items in loop with Timber and Twig

Often i need to do some tricky layout on dynamic elements like galleries.
Here's one example:
<ul>
<li class="slide">
<img src="img_01.jpg">
<img src="img_02.jpg">
</li>
<li class="slide">
<img src="img_03.jpg">
<img src="img_04.jpg">
</li>
<li class="slide">
<img src="img_05.jpg">
<img src="img_06.jpg">
</li>
</ul>
I've managed to do it with the following snippet. But i wanted some suggestions if possible about how to make it more flexible or more simple, like grouping by any number. Maybe using cycle() or any other method. I was getting strange results using the slice() or array[1:2] notation.
<ul>
{% for image in gallery %}
{% set current = loop.index %}
{% set next = current + 1 %}
{% if current is odd %}
<li class="slide">
{% for image in gallery %}
{% if loop.index in [current,next] %}
{% set th = TimberImage(image) %}
<img src="{{th.src}}">
{% endif %}
{% endfor %}
</li>
{% endif %}
{% endfor %}
</ul>
Any suggestions are welcomed.
Timber becomes very handy for quick in and out fixes with Timber::compile or custom themes with full routing. The purpose of the question is to create some snippet that can be reused.
Kudos to creators.
https://github.com/timber/timber
You can approach with the rest of the division with the following code (Here a working solutions):
{# number of element for every section #}
{% set section = 2%}
<ul>
{% for image in gallery %}
{% if loop.index % section == 1 %}
<li class="slide">
{% endif %}
{% set th = TimberImage(image) %}
<img src="{{th.src}}">
{% if loop.index % section == 0 or loop.last %}
</li>
{% endif %}
{% endfor %}
</ul>
You can easily reuse this code making a Twig macro using as parameter the gallery and the number of element for section (highlighted with the variable section
Here's the final result taking the suggestion of #Matteo for a macro:
https://gist.github.com/lithiumlab/5ee0454b0a77b1cc26fc0ce8ba52fd80
views/single.twig:
{% import 'utils.twig' as utils %}
{{utils.group_collection(gallery,3)}}
views/utils.twig:
{% macro group_collection(collection, groupby) %}
{% set section = groupby|default(2) %}
<ul>
{% for element in collection %}
{% if loop.index % section == 1 %}
<li class="group">
{% endif %}
{% set th = TimberImage(element) %}
<img src="{{th.src}}">
{% if loop.index % section == 0 or loop.last %}
</li>
{% endif %}
{% endfor %}
</ul>
{% endmacro %}

Resources