Can you iterate a front matter collection variable in Jekyll? - jekyll

I'm trying to see if it's possible to iterate a variable in Jekyll for a testimonial block I'm implementing for a Jekyll site. Basically, I'd like to have an icon be multiplied by the number dictated in my collection. Is this even possible with liquid markdown? Here's a snippet:
{% assign star = "<i class="icon-star"></i>" %}
{% assign star = star | times:{{ testimonials.stars }} %}
I'm thinking there's better ways to do this, but I was curious what I could get away with front matter.

To do it iterating, you can use a for loop appending the desired string to a variable:
{% assign star = '<i class="icon-star"></i>' %}
{% assign result = '' %}
{% for time in testimonials.stars %}
{% assign result = result | append: star%}
{% endfor %}

The key is to use a for block. You don't have to write multiple assign statements.
The following will render a star per the front matter key rating in any page using the code.
Modify the for bock as required.
{% assign star = '<i class="icon-star"></i>' %}
<div class="rating">
{% for count in (1..page.rating)) %}
{{ star }}
{% endfor %}
</div>
Ref: docs

Related

Jekyll/liquid list parameter?

I wanted to create a simply include which simplifies creating a carousel. So I was looking for the syntax to give a list of image urls as a parameter. However I couldn't find anything about this.
{% include carousel.html images=WHAT HERE? %}
You can create a list as a string then convert it into an array inside your snippet. This is the way I would do it.
{% assign urls = 'url1.jpg,url2.jpg,url3.jpg' %}
{% include carousel.html images=urls %}
Of course this is the same as:
{% include carousel.html images='url1.jpg,url2.jpg,url3.jpg' %}
carousel.html
{% assign images = include.images | split: ',' %}
{% for image in images %}
{{ image }}
{% endfor %}

jekyll assign concat in a loop?

I would like to organize a page based on the number of pages that pass a filter.
I have tried to append truthy pages to a collection but it doesn't work.
{% assign homepage_posts = [] %}
{% for my_page in site.pages %}
{% if my_page.homepage %}
{% assign homepage_posts = homepage_posts | concat: [my_page] %}
{% endif %}
{% endfor %}
<h1>size{{homepage_posts.size}}</h1>
<h1>{{homepage_posts}}</h1>
This is not working. Does concat only work with strings?
Jekyll will use Liquid 4 soon. But, for now, no concat.
In your case you can :
Create an empty array (bracket notation doesn't work in liquid) : {% assign homepage_posts = "" | split:"/" %}
{{ homepage_posts | inspect }} --> output : []
And push elements in it :
{% for my_page in site.pages %}
{% if my_page.homepage %}
{% assign homepage_posts = homepage_posts | push: mypage %}
{% endif %}
{% endfor %}
{{ homepage_posts | inspect }}
concat filter only works with arrays and will be available in Jekyll when it upgrades to Liquid 4.*:
concat
Concatenates (combines) an array with another array. The resulting
array contains all the elements of the original arrays. concat will
not remove duplicate entries from the concatenated array unless you
also use the uniq filter.
To filter pages containing a specific attribute (in this case homepage: true) you can use a where filter.
Having a page with front matter:
---
homepage: true
---
Then you can have the pages with the homepage: true attribute like:
{% assign homepages = site.pages | where:"homepage","true" %}

How to pass a frontmatter value into a for loop

I want to use a value in my frontmatter to specify a data file to loop through, but I can't get this to work.
I have a data file in _data/sidebars/sidebar1.yml. It contains:
- first
- second
- third
On a page I have this frontmatter:
---
title: My page
sidebar: site.data.sidebar.sidebar1
---
I want to use this code:
{% for entry in page.sidebar %}
* {{entry}}
{% endfor %}
However, this doesn't work. I've tried a number of things (involving assign and capture tags to define the page.sidebar content, but nothing seems to work).
The only thing that works is to do this:
{% if page.sidebar == "site.data.sidebars.sidebar1" %}
{% assign sidebar = site.data.sidebars.sidebar1 %}
{% endif %}
{% for entry in sidebar %}
* {{entry}}
{% endfor %}
However, I would like to avoid this extra code with the if statement, since it's easy to forget this and I would like to automate this more.
Any ideas on how to make this work?
You have a typo in your front matter. It's :
sidebar: site.data.sidebars.sidebar1
not
sidebar: site.data.sidebar.sidebar1
You can even be less verbose.
In your front matter : sidebar: sidebar1
In your code :
{% assign sidebar = site.data.sidebars[page.sidebar] %}
{% for entry in sidebar %}
{{ entry | inspect }}
{% endfor %}

How to get datas with multiple variables in path with jekyll and liquid

At its most basic level I need to append a partial object path onto an existing object path. In this particular instance I can't use plugins.
Say you have an object path:
{{ site.data.grants.2015.Return.ReturnHeader.ReturnTypeCd }}
Which, of course, can also be referenced as follows:
{% assign var = "ReturnTypeCd" %}
{{ site.data.grants.2015.Return.ReturnHeader[var] }}
How would I go about adding additional levels of nesting to the variable?
{% assign xTest = "Return.ReturnHeader.ReturnTypeCd" %}
{{ site.data.grants.2015[xTest] }}
//does not work
I've played around with both dot and bracket notations and using append as well as capture, but can't seem to find a solution that works.
This works :
Data file is _data/grants.yml
"2015":
Return:
ReturnHeader:
ReturnTypeCd: "Et hop !"
Getting deep target with a "dotted" string :
{% assign dataPath = site.data.grants.2015 %}
{% assign target = "Return.ReturnHeader.ReturnTypeCd" %}
{% comment %} ++++ Transform target string to an array {% endcomment %}
{% assign labels = target | split:"." %}
{% comment %} ++++
Looping in labels array and reassigning dataPath on each loop.
This goes deeper and deeper in the data tree
++++ {% endcomment %}
{% for label in labels %}
<h2>Label : {{ label }}</h2>
{% assign dataPath = dataPath[label] %}
<p>dataPath : {{ dataPath }}</p>
{% endfor %}

how to get a sorted tags_list in jekyll

I'm using jekyll-bootstrap to maintain a blog on GitHub.
I'd like to have a sorted tags_list. The tag with the most posts comes first. Then I can have a display that shows the first tags with bigger font-size and last tags with smaller font-size. And I also want a splice function.
If in python/Jinja2, I'd like some code like this:
{% for tag in sorted_tags[:10] %}
<li style="font-size:{{ tag.count }}px;">{{ tag.name }}</li>
{% endfor %}
What's the equivalent implementation in ruby/jekyll?
This is how I'm sorting by the number of posts in a tag (descending), without any plugins (i.e. GitHub Pages compatible).
It also works when your tag names contain spaces; only , and : are forbidden characters (but you can easily change those).
{% capture counts_with_tags_string %}{% for tag in site.tags %}{{ tag[1] | size | prepend:"000000" | slice:-6,6 }}:{{ tag[0] }}{% unless forloop.last %},{% endunless %}{% endfor %}{% endcapture %}
{% assign counts_with_tags = counts_with_tags_string | split:"," | sort | reverse %}
<ol>
{% for count_with_tag in counts_with_tags %}
{% assign tag = count_with_tag | split:":" | last %}
{% assign count = site.tags[tag] | size %}
<li>{{ tag }} ({{ count }})</li>
{% endfor %}
</ol>
It's super gross. What it does:
counts_with_tags_string is set to a string like 000005:first_tag,000010:second_tag,000002:third_tag. The zero-padded numbers are generated using the filter chain | prepend:"000000" | slice:-6,6.
This is split on commas and sorted lexicographically, which works because of the zero padding. The result is assigned to counts_with_tags.
Finally, we iterate over the elements and split each on : to find the original tag name. We could find the count in the same way, but because it's zero padded, it's easier to look it up using site.tags[tag] | size instead.
I thought that the tags array is sorted. Assuming so, you can do this:
{% for tag in site.tags %}
<li style="font-size: {{ tag[1].size }}px">{{ tag[0] }}</li>
{% endfor %}
That feels a little hacky, but it should work. Unfortunately, Liquid doesn't currently allow you to sort arrays within your templates. If you want to do any sorting on the array, you'd probably have to write a plugin to do so - it shouldn't be too complex. In fact, there's an existing plugin for sorting accessors that may do it: https://github.com/krazykylep/Jekyll-Sort
I only needed to do this in one place, on the page where my list of Tags was listed, so I wrote it as a Jekyll filter:
tag_index.html
<h2>Posts by Tag</h2>
<ul class="tags-list">
{{ site.tags | render_tags_list }}
</ul>
_plugins/filters.rb
module Jekyll
module Filters
def render_tags_list(tags)
sorted_tags = tags.keys.sort_by! { |tag| tag.downcase }
str = ''
sorted_tags.each { |tag|
str << '<li>' + tags[tag].size.to_s + ' - ' + tag + '</li>'
}
str
end
end
end
You could certainly just allow the filter to return sorted_tags above if you wanted to keep a better separation between "view" logic and programming logic, but my case was very simple. Trying to re-access a hash value by a specific key using Liquid templating wasn't a very concise process, or maybe I was just doing it wrong, but was much easier in Ruby.
I'm hosting my blog on github and wanted a solution to sorting a tag list that did not involve any jekyll plugins since Github doesn't allow for custom plugins (jekyll bootstrap attempts this as well). My post here doesn't really answer the question since I sort by tag name, and NOT by size. You can adapt this method to output the tag size as well in the string and then do some fancier spliting to get a different sort order (but it will be messy)
I was able to do this with the following code:
{% capture tagString %}{% for tag in site.tags %}{{ tag[0] }}{% unless forloop.last %}|{% endunless %}{% endfor %}{% endcapture %}
{% assign tags = tagString | split: '|' | sort: 'downcase' %}
<div id="cloud">
{% for tag in tags %}
{% assign number = site.tags[tag].size %}
{% assign slug = tag | downcase | replace: ' ', '_' %}
<span class="{% if number == 1 %}small{% elsif number <= 5 %}medium{% elsif number <= 10 %}large{% else %}huge{% endif %}">
{{ tag | downcase }}
</span>
{% endfor %}
</div>
It's kind of odd since I capture a string of tags (using | as separator) and then use that to create an array. After that point (in the loop) I can refer to the tag as tag and the list of sites that use that tag as site.tags[tag].
I use this on my blog:
https://github.com/kelsin/kelsin.github.io/blob/master/tags/index.html
The rest of the code is just how I've chosen to make a tag cloud on my tag page. Hope this helps someone else looking for a solution (without plugins)!