Nested Unless tags break Liquid/Jekyll - collections

I've been working with generating some JSON data from liquid tags, it seems that excluding the last comma in this JSON data is not happening when nesting 'unless' tags.
Please see answer below, this initial issue and question was not the problem.
<script id="suggest-json" type="application/json">
[
{%- for item in site[page.collection] -%}
{%- unless item.url == page.url -%}
{% assign images = item.media | where: "type", "image" %}
{% assign image = images.first %}
{
"url": "{{site.baseurl}}{{ item.url }}",
"title": "{{ item.title }}",
"subject": "{{ item.game }}",
"image": "{{ image.thumbnail }}",
"alt": "{{ image.thumbnail_alt }}"
} {%- unless forloop.last -%},{%- endunless -%}
{%- endunless -%}
{%- endfor -%}
]
</script>
Undesired Output (The last comma is invalidating the Json):
[
{
"url": "/portfolio/levels/low-life/",
"title": "Low-Life",
"subject": "Final Verdikt: Source",
"image": "https://picsum.photos/445/296?random=1",
"alt": "Image 1"
},
{
"url": "/portfolio/levels/prospekt/",
"title": "Prospekt",
"subject": "Final Verdikt: Source",
"image": "https://picsum.photos/445/296?random=1",
"alt": "Image 1"
},
{
"url": "/portfolio/levels/station/",
"title": "Station",
"subject": "Half-Life 2: Opposition",
"image": "https://picsum.photos/445/296?random=1",
"alt": "Image 1"
},
{
"url": "/portfolio/levels/dredge/",
"title": "Dredge",
"subject": "Firearms: Source",
"image": "https://picsum.photos/445/296?random=1",
"alt": "Image 1"
},
{
"url": "/portfolio/levels/gravitas/",
"title": "Gravitas",
"subject": "Firearms: Source",
"image": "https://picsum.photos/445/296?random=1",
"alt": "Image 1"
},
{
"url": "/portfolio/levels/navarro/",
"title": "Navarro",
"subject": "Firearms: Source",
"image": "https://picsum.photos/445/296?random=1",
"alt": "Image 1"
},
{
"url": "/portfolio/levels/deadlock/",
"title": "Deadlock",
"subject": "Half-Life 2: Opposition",
"image": "https://picsum.photos/445/296?random=1",
"alt": "Image 1"
},]
I've tried If statements, assigning booleans for isLast index, spacing and other formatting but nothing works...
Answer
The main issue here was separating my array into a new suggestions array minus the filtered item, which makes forloop.last actually happen. The other way was just ignoring the filtered item, but considering it still part of the array, which is why forloop.last was never true.
I also use {% raw %} tag to escape the JSON brackets for safe measure. I thought this might be causing Jekyll to try and process them as a tag, leading to an error with no actual error output in the --verbose build logs. Although I think the main culprit was the need to create a separate array to loop through.
{% assign suggestions = "" | split: ',' %}
{%- for item in site[page.collection] -%}
{%- unless item.url == page.url -%}
{% assign suggestions = suggestions | push: item %}
{%- endunless -%}
{%- endfor -%}
<script id="suggest-json" type="application/json">
[
{%- for item in suggestions -%}
{% assign images = item.media | where: "type", "image" %}
{% assign image = images.first %}
{% raw %}
{
{% endraw %}
"url": "{{ site.baseurl }}{{ item.url }}",
"title": {{ item.title | jsonify }},
"subject": {{ item.game | jsonify }},
"image": {{ image.thumbnail | jsonify }},
"alt": {{ image.thumbnail_alt | jsonify }}
{% raw %}
}
{% endraw %}
{%- unless forloop.last -%},{%- endunless -%}
{%- endfor -%}
]
</script>

Related

How to print common capital letter for posts 11ty?

I have posts in my 11ty blog - books, whose names started on some letters. For example, i have 12 books: 4 with first letter "A", 4 with first letter "F", 4 with first letter "Q".
And I have a code for printing all books (I use njk in blog layouts):
<div>
{% for book in collections.books %}
<a class="book" href="{{ book.url }}">
<article>
<h3 class="text-link">
{{ book.data.name }}
</h3>
<p>{{ book.data.text }}</p>
</article>
</a>
{% endif %}
{% endfor %}
</div>
I need to print in front of all books with the letter "A":
<h2>A</h2>
Аfter that there will be 4 books with the letter "A". And after this books, will be the same heading, but with letter "F":
<h2>F</h2>
How to do it? In njk, it is difficult to assign/rewrite and manipulate variables. Maybe there is some normal example?
You can use Nunjucks's set tag to create and modify a variable that keeps track of the previous initial letter:
<div>
{% set previousInitial = null %}
{% for book in collections.books %}
{% if book.data.name[0] != previousInitial %}
<h2>{{ book.data.name[0] }}</h2>
{% endif %}
{% set previousInitial = book.data.name[0] %}
<a class="book" href="{{ book.url }}">
<article>
<h3 class="text-link">
{{ book.data.name }}
</h3>
<p>{{ book.data.text }}</p>
</article>
</a>
{% endfor %}
</div>
Demo:
const data = {
collections: {
books: [
{ data: { name: 'ABC Book', text: 'Learn the alphabets!' }, url: '/book/abc-book/' },
{ data: { name: 'Another A book', text: 'A nice book' }, url: '/book/another-a-book/' },
{ data: { name: 'Foo...', text: '...bar and baz' }, url: '/book/foo/' },
],
},
}
const template = `
<div>
{% set previousInitial = null %}
{% for book in collections.books %}
{% if book.data.name[0] != previousInitial %}
<h2>{{ book.data.name[0] }}</h2>
{% endif %}
{% set previousInitial = book.data.name[0] %}
<a class="book" href="{{ book.url }}">
<article>
<h3 class="text-link">
{{ book.data.name }}
</h3>
<p>{{ book.data.text }}</p>
</article>
</a>
{% endfor %}
</div>
`
console.log(nunjucks.renderString(template, data))
.as-console-wrapper { max-height: 100% !important; }
<script src="https://unpkg.com/nunjucks#3.2.3/browser/nunjucks.min.js"></script>
If the items in collections.books are not already sorted by their names, you can sort them with Nunjucks's sort filter:
{% for book in collections.books|sort(attribute='data.name') %}
An alternative is to create a new collection. Consider creating a collection of books already categorized by their initial letters, so you can reduce the amount of logic in your Nunjucks template. Example of how the new collection and template could look like:
const data = {
collections: {
booksCategorizedByInitialLetters: {
A: [
{ data: { name: 'ABC Book', text: 'Learn the alphabets!' }, url: '/book/abc-book/' },
{ data: { name: 'Another A book', text: 'A nice book' }, url: '/book/another-a-book/' },
],
F: [
{ data: { name: 'Foo...', text: '...bar and baz' }, url: '/book/foo/' },
],
},
},
}
const template = `
<div>
{% for initialLetter, books in collections.booksCategorizedByInitialLetters %}
<h2>{{ initialLetter }}</h2>
{% for book in books %}
<a class="book" href="{{ book.url }}">
<article>
<h3 class="text-link">
{{ book.data.name }}
</h3>
<p>{{ book.data.text }}</p>
</article>
</a>
{% endfor %}
{% endfor %}
</div>
`
console.log(nunjucks.renderString(template, data))
.as-console-wrapper { max-height: 100% !important; }
<script src="https://unpkg.com/nunjucks#3.2.3/browser/nunjucks.min.js"></script>
(I'm not sure whether it's possible to create a collection whose type is an object, but IIRC it should be possible.)

Shopify - capture of Handlebars result not equal in liquid if

So I am able to capture a handlebars result into a liquid variable and output the value, documentation states it is always a string. When I test the captured variable against another string variable that should be equal it always returns false. Not sure why this isn't working, anyone able to lend an assist?
{%- raw -%}
{{#items}}
{{#each tags}}{%- endraw -%}
{%- capture strTag -%}{%- raw -%}{{ this }}{%- endraw -%}{%- endcapture -%}
Captured Tag: {{ strTag }} <!-- will spit out the tag, it is being captured -->
{%- assign test_tag = "test tag" -%}
{%- if strTag == test_tag -%} <!-- this never fires as true, but both strTag and test_tag will output as "test tag" -->
{{ strTag }} matched {{ test_tag }}
{%- else -%}
{{ strTag }} did not match {{ test_tag }} <!-- this is what always returns as "test tag did not match test tag" -->
{%- endif -%}{%- raw -%}
{{/each}}
{/items}}{% endraw %}

How can I get the data of an object in datatables?

I am getting data from a json file into my datatable.
"columns": [
{% for key, value in columns %}
{
"data": "{{ key }}"},
{% endfor %}
]
Like this I get the following output:
id name slug icon
2 Mitarbeiter members [object Object]
3 Angebote offers [object Object]
4 Produkte products [object Object]
5 Felder fields [object Object]
To recieve the data of the object, I changed my code to this:
"columns": [
{% for key, value in columns %}
{ "data": "{{ key }}.name",
"defaultContent": "{{ key }}"},
{% endfor %}
]
This is working well for the object, but now my other fields do not show the value anymore, the show the label of the column:
id name slug icon
id name slug icon
id name slug anchor
id name slug adjust
id name slug cloud
dump of columns:
array:5 [▼
"id" => ReflectionProperty {#6092 ▶}
"name" => ReflectionProperty {#6094 ▶}
"slug" => ReflectionProperty {#6096 ▶}
"icon" => ReflectionProperty {#6097 ▶}
]
Another approach is this:
"columns": [
{% for key, value in columns %}
{% if key is iterable %}
{"data": "{{ key }}"},
{% else %}
{"data": "{{ key }}.name"},
{% endif %}
{% endfor %}
]
But here I get only the output of the icons row...
The json file is this:
[{"id":2,"name":"Mitarbeiter","icon":{"id":2,"name":"anchor"},"slug":"members"},{"id":3,"name":"Angebote","icon":{"id":1,"name":"adjust"},"slug":"offers"},{"id":4,"name":"Produkte","icon":{"id":1,"name":"adjust"},"slug":"products"},{"id":5,"name":"Felder","icon":{"id":1,"name":"cloud"},"slug":"fields"}]
Finally found a solution:
"columnDefs": [
{
"render": function (data, type, row) {
var type = typeof data;
if(type == "object"){
return data.name;
} else {
return data;
}
},
"targets": "_all"
}
],
"columns": [
{% for key, value in columns %}
{ "data": "{{ key }}"},
{% endfor %}
]
Truy to use: of_type('object')
"columns": [
{% for key, value in columns %}
{
"data": "{% if key is of_type('object') %}{{ key }}.name{% else %}{{ key }}{% endif %}"},
{% endfor %}
{ "data": "id" }
]

How can I set a variable with twig?

I want to set the variable key before using it
"columns": [
{% for key, value in columns %}
{"data": "{{ key }}"},
{% endfor %}
{ "data": "id" }
]
My approach:
"columns": [
{% for key, value in columns %}
{% set result = '{{ key }}' %}
{"data": "{{ result }}"},
{% endfor %}
{ "data": "id" }
]
But it is not working. I get the error message:
Requested unknown parameter '{{ key }}'
{% set result = key %}
From what I see, you want to set the value of result variable, result of key is already set, you just didn't access it correctly.

How can I check multiple if conditions in twig?

How can I check multiple if conditions in twig?
Neither of these seem to work
Also they all seem a but messy and heavy.
{% if pageClass != "page-home" %}
{% if bodyClass != "500" %}
{% if bodyClass != "404" %}
{% include '_components/type-bg' with {
content: {
slug: entry.slug|split(' ')|slice(0, 1)|join
},
} only %}
{% endif %}
{% endif %}
{% endif %}
I have also tried the below
{% if (pageClass != "page-home") or (if bodyClass != "500") or (if bodyClass != "404")%}
{% include '_components/type-bg' with {
content: {
slug: entry.slug|split(' ')|slice(0, 1)|join
},
} only %}
{% endif %}
You need to use and as your all condition need to satisfy to execute that code so it must be and
{% if pageClass != "page-home" and bodyClass != "500" and bodyClass != "404" %}
{% include '_components/type-bg' with {
content: {
slug: entry.slug|split(' ')|slice(0, 1)|join
},
} only %}
{% endif %}
Better solution would be from code block just use PHP code [you can do all PHP stuff here] to achieve this using switch case or etc .. and pass only flag like needToInclude as boolean to view and just use that.
{% if needToInclude %}
{% include '_components/type-bg' with {
content: {
slug: entry.slug|split(' ')|slice(0, 1)|join
},
} only %}
{% endif %}
if any doubt please comment
The correct check would be the following
{% if pageClass != 'page-home' and bodyClass not in [ 500, 404, ] %}
Meaning execute when the pageClass is not the home page and make sure the bodyClass is not an erroneous state

Resources