Loop over document properties - jekyll

In my markdown header I have added a custom line:
---
layout: docs
title: "My title"
date: 2015-09-18 22:40:58
permalink: /some/url/
custom: valueA valueB <---
---
And I managed to write the following template which processes these values:
{% capture custo %}{{page.custom}}{% endcapture %}
{% assign cust = custo|split: %}
{% for cus in cust%}
<code>{{ cus }}</code>
{% endfor %}
However, this seems much too complex to me. I have tried
moving the assignment directly into for, which compiles but just outputs everything as one value, not as separate ones
{% for cus in custo|split: %}
getting rid of capture, but I get undefined method 'split' for nil:NilClass doing
{% assign cust = page.custom|split: %}
Can my template be simplified or does it need to be that way? Or is it even the wrong approach?

Doing more research on this, I found out that the header is not only a header but actually YML. Therefore I can do
---
layout: docs
title: "My title"
date: 2015-09-18 22:40:58
permalink: /some/url/
custom:
1: valueA
2: valueB
---
and then use an ordinary loop
{% for cus in page.custom %}
<code>{{ cus[1] }}</code>
{% endfor %}

Related

Why does this jekyll template not work?

Short Version:
Why does the following code not produce an output when navbox.next_article is the string '2018-01-05-man-command'?!
{% capture np %} {{ site.posts | where:"post","navbox.next_article contains post.title" }} {% endcapture %}
The next post is {{ np.title }}
Details
My post 2018-01-05-man-command.md has a YAML front matter:
---
layout : post
title : 'Man Command'
tags : [RHCSA, RHCSA_mod, Using Essential Tools, Man Command]
categories: [RHCSA]
navbox:
# prev_article:
next_article: 2018-01-05-understanding-globbing-and-wildcards
---
This is accessed by the _includes/post.html file through:
{% unless include.excerpt %}
{{ post.content }}
{% include navbox.html navbox=page.navbox %}
{% endunless %}
This is used by the _layout/post.html which sets the layout for the post:
{% include post.html post=page link_title=false %}
My navbox.html contains:
{% assign navbox = include.navbox %}
{% capture np %} {{ site.posts | where:"post","navbox.next_article contains post.title" }} {% endcapture %}
The next post is {{ np.title }}
However, all I get when I run bundle exec jekyll serve is:
The next post is
Why does that line not work? I'm new to jekyll so it's possible I've made a blunder somewhere that's intuitive to most. Please tell me what I can fix.
I believe that the capture tag only captures strings, not posts. See here for more info.
I'm not convinced that a where filter supports the contains syntax you're using. See here for more info.
On top of that, where returns an array. You have to get the first item from that array.
You need to fix these issues. Use an assign instead of a capture to store a post. And change your where filter to not use the contains syntax, which isn't valid. (Unless it's been added since the issue I just linked.)
Here is how I've done it:
{% assign post = site.posts | where:"url", targetUrl | first %}

Is there a way to loop through the front matter of a jekyll collection?

I have a Jekyll collection which is not being output, but elements of which are being displayed on a single page, like this:
{% for element in site.collection %}
{{ element.content }}
{% endfor %}
I would like to be able to do something like this:
{% for element in site.collection %}
{{ element.content }}
{% for front_matter in element %}
<!-- do stuff -->
{% endfor %}
{% endfor %}
This is possible with YAML hashes in YAML _data files, but doesn't work here because, it seems {{ element }} on its own is identical to {{ element.content }}. There doesn't seem to be any variable designated for this either like, {{ element.front_matter }}. Is it possible to loop through the front matter of an element in a jekyll collection?
I know that the ideal way to do this would be to include all the front_matter I want to loop through in a variable, like:
---
front_matter:
foo: bar
bar: foo
---
But, as I'm trying to configure these pairings (foo and bar) to be easily updatable through prose.io, they can't be nested under other values. If there is a
way around this with prose though, I would accept that answer.
Much appreciated!
It is possible to loop through the variables of an element in a Jekyll collection:
{% for items in site.my_collection %}
{% for item in items %}
{{ item }}: {{ items[item] }}
{% endfor %}
{% endfor %}
But it is important to remember that there is other metadata that is also available and will be included in the iteration, like path, url, etc and your front matter, e.g. for _my_collection/something.md:
next:
path: _my_collection/something.md
output: <p></p>
url: /my_collection/something.html
id: /my_collection/something
content: <p></p>
collection: my_collection
relative_path: _my_collection/something.md
excerpt: <p></p>
previous:
draft: false
categories:
slug: something
ext: .md
tags:
date: 2017-05-23 14:43:57 -0300

Jekyll: Generator to link to all subpages in a static hierarchy

I'm trying to write a static site with Jekyll that has a few layers to it. What's the best way to generate links to all subpages within a section?
For example, if I have a site structure like this:
landing
- Topic A
- Content 1
- Content 2
- Content 3
- Topic B
- Content 1
- Content 2
- Content 3
What would be the best way to create links to each of the Content pages from its Topic page? And, is there a simple way to link to all the Topic pages from the landing?
These are not posts, just static pages. It would be really great if I could just do {% for topic.each %} ...etc. and print the links out.
I would not use posts for this purpose (as yaitloutou suggests). I would read the hierarchy from the directory structure (solution 1) or create two seperate collections (solution 2). You can let the collections from solution 2 share the same layout if you want that.
1. Using pages
Create a directory structure with index.md pages and loop over the Jekyll veriable called 'site.pages' to create the menu.
index.md
topic-a/index.md
content-1/index.md
content-2/index.md
content-3/index.md
topic-b/index.md
content-1/index.md
content-2/index.md
content-3/index.md
And loop over all pages like this:
<ul>
{% assign sitepages = site.pages | sort: 'order' %}
{% for sitepage in sitepages %}
<li {% if page.url == sitepage.url %} class="active"{% endif %}>
{{ sitepage.title }}
</li>
{% endfor %}
</ul>
If you want the nested structure, you can do something like this. Or if you want only the results for Topic A, you can do this:
<ul>
{% assign sitepages = site.pages | sort: 'order' %}
{% for sitepage in sitepages %}
{% if sitepage.url contains 'topic-a' %}
<li {% if page.url == sitepage.url %} class="active"{% endif %}>
{{ sitepage.title }}
</li>
{% endif %}
{% endfor %}
</ul>
2. Using collections (simplest solution and quickest build)
Create a collection Topic A and create another collection Topic B. Your config file should look like this:
collections:
topic-a:
output: true
permalink: /topic-a/:path/
topic-b:
output: true
permalink: /topic-b/:path/
Outputting the items of one topic goes like this:
{% assign atopics = site.topic-a | sort: 'order' %}
{% for atopic in atopics %}
<li {% if page.url == atopic.url %} class="active"{% endif %}>
{{ atopic.title }}
</li>
{% endfor %}
</ul>
You should create a _topic-a and a _topic-b directory with your content-1.md, content-2.md, etc. files.
Note that both solutions have YML variables called 'order', to determine the order of appearance of the items/pages. This looks like this:
---
title: mytitle
layout: mylayout
order: 50
---
mycontent
I'll propose here 2 ways, you can determine the "best" according to your specific needs/situation, and which one sound more adapted to them.
first of all, "posts" and "pages" are basically just collections of md/html files. with some variables associated to each one.
to generate files with this structure, you can:
1. Using _posts and page.categories
put all the sub-files in _posts (the 2017-01-01- is just a place holder)
_posts/
- 2017-01-01-content-a-1.md
- 2017-01-01-content-a-2.md
- 2017-01-01-content-a-3.md
- 2017-01-01-content-b-1.md
- 2017-01-01-content-b-2.md
- 2017-01-01-content-b-3.md
add appropriate categories to each file:
2.1. for posts caontent-a-* add category: topic-a (in this order) by adding this line in the yaml front matter at top of each of them:
---
layout: page # or any appropriate layout
category: topic-a
---
2.2. for posts caontent-b-* add category: topic-b
set a premalink to ignore the date, and create the desired structure, by adding the following line to _config.yml:
defaults:
-
scope:
path: "_posts" # to all the file in posts
values:
permalink: /landing/:categories/:title.html # set this as default permalink value
you still can specify a permalinks per post in its front matter, or just add the permalink line to each md folder front matter.
the above will generate the desired structure.
loop through all the
{% for entry in site.posts %}
{% if entry.category == type-a %}
<!-- do A stuff -->
{% elsif entry.category == type-b %}
<!-- do B stuff -->
{% endif %}
{% endfor %}
2. Using collections:
it's similar to the above, but instead of using the already existent _postscollection you'll start by creating a new collection (one advantage is that you'll not need to add a date )
any of the approaches above will generate this structure inside _site
landing/
type-a/
content-a-1/
index.html
content-a-2/
index.html
...
type-b/
...

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 %}

Filtering an array in a Liquid/Jekyll template

Most Liquid "filters" are actually "map"s in the functional programming sense: you take an array, you apply a function to each element and return the transformed array. I'd like instead to "filter": I want to return only those items in the array that match a certain condition. How can I do that?
Specifically, I'm trying to improve this template:
Authors:
{% for c in site.data["contributors"] %}
{% assign contributor = c[1] %}{% if contributor.role contains "author" %}{{contributor.name.given}} {{contributor.name.family}}{% endif %}{% endfor %}
which, prettified, looks like this:
Authors:
{% for c in site.data["contributors"] %}
{% assign contributor = c[1] %}
{% if contributor.role contains "author" %}
{{contributor.name.given}} {{contributor.name.family}}
{% endif %}
{% endfor %}
where its data looks like this:
ianbarber:
name:
given: Ian
family: Barber
org:
name: Google
unit: Developer Relations
country: UK
role:
- engineer
homepage: http://riskcompletefailure.com
google: +ianbarber
twitter: ianbarber
email: ianbarber#google.com
description: "Ian is a DRE"
samdutton:
name:
given: Sam
family: Dutton
org:
name: Google
unit: Developer Relations
country: UK
role:
- author
google: +SamDutton
email: dutton#google.com
description: "Sam is a Developer Advocate"
(example taken from here).
The problem with this approach is that a newline is output if the current element doesn't match the condition, as you can see in https://developers.google.com/web/humans.txt.
How can I fix this?
Jekyll 2.0 has a where filter. See docs. Example - from doc:
{{ site.members | where:"graduation_year","2014" }}
{{ site.members | where_exp:"item", "item.graduation_year == 2014" }}
If you want to get rid of the newline if the condition doesn't match, try to remove the unwanted newlines in the if and for clauses:
{% for c in site.data["contributors"] %}{% assign contributor = c[1] %}{% if contributor.role contains "author" %}
{{contributor.name.given}} {{contributor.name.family}}{% endif %}{% endfor %}
That may not look nice in the source, but it will probably get rid of the newline in the output.
Note: I didn't try this, but similar rearranging helped me get rid of unwanted newlines. You may even have to put the {% if ... on the extreme left, so the indentation does not get included.
#Rudy Velthuis answer
But indented.. (Could not edit answer as the queue is full)
{% for c in site.data["contributors"] %}
{% assign contributor = c[1] %}
{% if contributor.role contains "author" %}
{{contributor.name.given}}
{{contributor.name.family}}
{% endif %}
{% endfor %}
Why liquid community so agains indentation I do not know..