How to dynamically assign _data variable? - jekyll

I'm using staticman to enable comments on my blog. It puts the comments in the _data folder. My folder structure then looks like this:
_data/
comments/
blog-post-1/
entry1542891129928.yml
...
blog-post-2/
entry1542891129928.yml
...
...
In my _layouts/post.html I want to access comments for a specific blog. This is the code that I expect to work to get to the comments:
{% assign comments = site.data.comments[page.slug] | sort %}
But when I run build, I get the following error:
Liquid Exception: Liquid error (line 39): Cannot sort a null object. in /_layouts/post.html
It seems to be something to do with page.slug because if I replace it with the string 'blog-post-1' it works.
How to get the post slug dynamically in post.html?

Solved the problem!
The issue is when the folder does not exist. I circumvent this by moving the sort filter:
{% assign comments = site.data.comments[page.slug] %}
{% if comments %}
{% assign comments = comments | sort %}
...do things...
{% endif %}
Now the build doesn't fail.

Related

Can I get posts by using an array of tags using Jekyll?

I'm working on a liquid script for my Jekyll website to retrieve related posts. I'd like to use the array of tags used for the current post (post.tags) to do a query that matches other posts on the site that also have one (or more) of those tags. Such as:
assign relatedPosts = site.posts | where 'tags', page.tags
However, in the documentation it only mentions passing in a string, not an array of strings? I would ideally like to accomplish this using the where operator (or something similar), instead of iterating through every post on the site.
You are correct that you can't supply an array for the where operator, as per the documentation, you can only pass a string there.
By the nature of your requirement, it looks like iterating through every post on the website is not something you can avoid, since basically you want to check all your posts for a couple of different tags.
One possible solution would be to iterate through your post tags first, then check that tag against all site post tags, something like this:
{% assign new_array = "" | split: "" %}
{% for tag in page.tags %}
{% assign tag_found = site.posts | where "tags": tag %}
{% assign new_array = new_array | concat: tag_found %}
{% endfor %}
OUTPUT: {{ new_array }}

Searching for page in site.pages by path without iteration

I have a path to some page from root in Jekyll as a variable path. I want to get some variables from FrontMatter of that page. How could I find this page in site.pages without iterating over all pages?
I mean something like
{% assign aim = site.pages[path] %}
instead of
{% for p in site.pages %}
{% if p.path == path %}
{% assign aim = p %}
{% endif %}
{% endfor %}
Will this solution be faster for a site with a thousand of pages?
You can use the where liquid filter for this:
{% assign aim = site.pages | where:"path",path %}
Thanks #β.εηοιτ.βε for the hint!
That was what I expected, but when I used this template, I've faced the problem: the output of {{aim.title}} was empty.
The where filter produces list even it consists of just one element! But each filepath in a file system points exactly at one file, so I expected that aim will be the page, not a list. To fix this, I've added first filter:
{% assign aim = site.pages | where:"path",path | first %}
And now aim is the page variable I am searching for.
About speed. This solution builds a site 2X faster on my hardware.

Trying to get a collection from a Front Matter value

Hi I'm new to Jekyll and have a basic blog set up. Now my idea is to loop through a collection per blog post to get some fancy HTML going on. Because as far as I'm aware you can't simply add HTML to the markdown.
// config.yml
collections:
- hollowknight
So I've set up a collection named hollowknight and I got a blog post about Hollow Knight.
In the Front Matter of the blog post I have collectionid: 'hollowknight'. And in the Layout I use for blog posts I tried the following:
// post.html
{% capture collection %}site.{{ page.collectionid }}{% endcapture %}
{% for section in collection %}
{{ section.title }}
{% endfor %}
So the idea was to set the collection variable to site.hollowknight as configured in the config and the Front Matter. I set up a single article inside the collection, but it isn't showing up.
Is there any way to realise this?
Edit: https://talk.jekyllrb.com/t/list-collection-with-name-from-front-matter/3619/6
Or is there a way to use paragraphs in the Front Matter, because then I could just use arrays and objects in the Front Matter of the posts to do my magic?
Edit: https://github.com/jekyll/jekyll/issues/246#issuecomment-1639375
Or am I just stretching to far from what Jekyll is supposed to be?
Edit
So I found out I was getting the collection the wrong way, https://talk.jekyllrb.com/t/list-collection-with-name-from-front-matter/3619/6.
{% for section in site.[page.collectionid] %}
{{ section.content | textilize }}
{% endfor %}
The only weird part is, everywhere I see | textilize being used but can't get it to work.
This is the final solutions:
{% for section in site.[page.collectionid] %}
{{ section.content | markdownify }}
{% endfor %}
It's markdownify instead of textilize.
Also I ended up using a Front Matter array with objects to loop through instead of a collection to keep it more organized.

Listing related records in Bolt CMS

I have content types Topics and Pamphlets. Pamphlets are related to topics (e.g. a pamphlet called 'Mass in B minor' could be related to the topic 'music') using 'relations' field-type:
pamphlet:
...
relations:
topics:
multiple: false
...
What I want is to list all the Music pamphlets. More exactly, I want to be able to display each topic, with a list of pamphlets related to that topic.
There's a function record.related() but it works the other way, that is, I can use it when I display a pamphlet to show that the pamphlet is related to Music; but I can't use it when I display the Music topic to list all the pamphlets that are related to music.
(The documentation says that relations are always bi-directional, but I can't see how.)
Suggestions welcomed.
I was simply wrong when I said that the related records() function didn't do the job. Here we are (nicked from the out-of-the-box record.twig template):
{% set relatedrecords = record.related() %}
{% if relatedrecords is not empty %}
<p> Examples:</p>
<ul>
{% for related in relatedrecords %}
<li><a href="{{ related.link }}">
{{ related.title }}</a></li>
{% endfor %}
</ul>
{% endif %}
In general, it seems, if A (a pamphlet) is related to B (a topic), exactly the same method can be used to show either the topic to which a given pamphlet belongs, or the pamphlets belonging to a given topic.
Apologies for getting this wrong, and congrats to the Bolt developers for a neat bit of work.
I now have an inefficient solution. Maybe someone can improve it.
To display all the pamphlets related to a given topic, I created a template topic.twig. Within that template, record.title contains the name of the topic (eg 'Music') and record.id contains the ID of the topic. We need to select pamphlets that are marked as related to a topic with that ID. The following code fetches all the pamphlets into pamphletlist then considers each one. Using the valuable dump() function, I found that the data structure for a pamphlet includes an array relation which includes an array topics. The code below works because in my data, one pamphlet can only be related to one topic. Code:
{# to get a list of all pamphlets related to this topic #}
{% setcontent pamphletlist = 'pamphlets' %}
{% for p in pamphletlist %}
{{ dump(p) }}
{% for t in p.relation.topics %}
{% if record.id == t %}
<p> {{ p.title }} </p>
{% endif %}
{% endfor %}
{% endfor %}
It would surely be more efficient to use a where clause, something like this:
{% setcontent pamphletlist = 'pamphlets' where { relation.topics: ... } %}
That would presumably get the election done at the database level, instead of peeking at each record in turn. But my experiments only produced twig syntax errors. Does anyone have a more efficient method?
Have you looked at the Related Content by Tags extension?
This gives you a list of related content by tags:
http://extensions.bolt.cm/view/45073af0-8585-4d5b-b978-fd6405858e0e

Page variable in for loop file name

For a bilingual website, i have yaml data files for 2 languages.
files example:
en_services.yml
fr_services.yml
Variable example in my page:
---
lang: en
---
I want to loop trough the file with the lang as the prefix, something like that:
{% for service in site.data.{{ page.lang }}_services %}
{% endfor %}
This doesn't work, is there a way I can do that?
By the way, I don't think I can add subfolders in the _data folder, right?
Thanks.
While that doesn't work, if you are able to put them both in the same file (grouped under the appropriate language code) there is a solution.
This gist was for another example based on post authors, but your should be able to use the same setup using language codes instead of author names.
You should use:
site.data[{{ page.lang }} + '_services']
data[i] has key/value entries for all the files in your _data directory, and passing the string in, like so (for /_data/book.yaml):
site.data['book']
...totally works and opens up the possibility of concatenating strings inside the brackets with whatever variable you want. :)
{% capture thefile %}{{ page.lang }}_services{% end capture %}
{% for service in site.data[thefile] %}
...
{% endfor %}
Should do the trick.