In Jekyll you can use liquid template and I am trying to write a nav that includes all links in the website.
sitemap:
home: "/"
demo:
right: "/right"
left: "/left"
What I am trying to achieve is to create a sidebar that icludes all those links. But certains links are under a section. The output should be the following
<nav>
<ul>
<li>
<a href="/">home</a>
</li>
</ul>
<ul>
<li>demo</li>
<li>
<a href="/right">right</a>
</li>
<li>
<a href="/left">left</a>
</li
</ul>
</nav>
Not all sections must have a title. The home link is a standalone link. The demo links are all in the demo section.
In liquid I can loop through the sitemap in this way:
{% for nav in site.sitemap %}
<ul>
<li>{{ nav[0] }}</li>
</ul>
{% endfor %}
In this way, liquid will print home and demo.
What I need is to check if the value is a string or an array in order to print the array or a single link!
Is there a way to check if the liquid variable is a string or an array? I can't find it in the documentation i linked before!
Is there a way to check if the liquid variable is a string or an array?
To check if a liquid variable is a string or a list of elements (array or hash), check if it has a first descendant, using the array filter first: {% var.first %} as follow :
{% if var.first %}
// var is not a string
{% else %}
// var is a string
{% endif %}
the filter first return the first element of an array or a hash if it has one, if the variable is empty or not a list of element it return nothing, and since nil is a falsy value in liquid, it works the same as false in the if condition above.
e.g :
# var1 = "a" // string
{% var1.first %} // return: // nil -> falsy
# var2 = [a,b,c] // array
{% var2.first %} // return: a
# var3 = {k1: a, k2: b, k3: c} // hash
{% var1.first %} // return: k1a
# var4 = {k1, k2, k3: c} // hash, first element is a key without associated value
{% var1.first %} // return: k1
Now that we can determine if an element is a string or not, we can do that:
{% for nav in site.sitemap %}
<ul>
<li>
{{ nav[0] }} :
{% if nav[1].first %}
// loop through: nav[1]
{% else %}
{{ nav[1] }}
{% endif %}
</li>
</ul>
{% endfor %}
but I found that's more convenient* to work the sitemap as list of elements (a), instead a single big hash (as it is actually) (b) (cf. YAML syntax)
so I modified it's structure, by adding a "- " (a dash and a space) before each element:
- home: "/"
- demo:
- right: "/right"
- left: "/left"
which give us a:
{"home"=>"/"}{"demo"=>[{"right"=>"/right"}, {"left"=>"/left"}]}
instead of b:
{"home"=>"/", "demo"=>{"right"=>"/right", "left"=>"/left"}}
Side Note: you can put the sitemap in it's own file in: _data/sitmap.yml and access it by site.data.sitemap, in lieu of defined it as an attribute in the _config.yml, to keep it cleaner, thereby it'll look exactly as the above.
since we'll going to include the sitemap in itself recursively (for each node with children ) we'll put this template in _includes folder and not _layouts
Notice also that Jekyll allows passing parameters to includes. But there is a catch, the param should be a string not an array, or hash. So, we have to make a string out of the links array.
here we go:
<ul>
<!-- links is a param -->
{%for link in include.links %}
<li>
{% for part in link %}
{{part[0]}} : <!-- part[0] : link name -->
{% if part[1].first %} <!-- the element has children -->
<!-- concatenate jekyll array into a string -->
{% assign _links = "" | split: "" %}
{% for _link in part[1] %}
{% assign _links = _links | push: _link %}
{% endfor %}
<!-- pass the string as a param to sitemap, then do the recursive dance -->
{% include sitemap.html links = _links %}
{% else %} <!-- no children -->
{{part[1]}} <!-- part[1] : link url -->
{% endif %}
{% endfor%}
</li>
{%endfor%}
</ul>
<!-- include and init the param with this ↓, or `site.sitemap` if it's defined in `_config.yml`, or ... -->
{% include sitemap.html links= site.data.sitemap %}
for: /_data/sitemap.yml:
- home : "/"
- about: "/about"
- archive:
- left : "/left"
- right: "/right"
- other:
- up: '/other/up'
- down: '/other/down'
the template produce:
so you have to put part[0] and part[1] of each link in an <a> tag like:
<a href="{{part[1]}}"> {{part[0]}} </a>
I'm using Jekyll 2.8.5 and
var[0]
is falsy for string values, but truthy for an array (whose first element is truthy).
(On the other hand var.first, as recommended by the previously accepted answer, now returns the first character of the string, and so it doesn't seem to be a good way to determine if a value is an array anymore.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With