Errors with nesting macros and passing a collection through them? Using Eleventy with Nunjucks - nunjucks

Somewhat of a more advanced question here... is it possible to pass a collection down through a few macros? For example, I have a blog posts section:
{% from "components/switchers/topCoupons.njk" import topBonuses %}
{{ topCoupons(
title = "Top Coupons",
blurb = "some body text content",
posts = collections.coupon
) }}
Then within the posts macro, I have a slider macro:
{% from "components/sliders/generalSlider.njk" import generalSlider %}
{{ generalSlider(
slides = posts
) }}
Then within the slider macro, I have a card macro:
{%- for slide in slides -%}
{% from "components/cards/card.njk" import card %}
{{ card(
title = posts
) }}
{%- endfor -%}
At the moment it is not working but I'm wondering how could I approach this situation and whether Eleventy and Nunjucks even offer this type of functionality, what the solution would be, or if I'm better off using another SSG that would have this kind of infrastructure?
At the moment, it is throwing this error when trying to compile:
[eleventy:dev] `TemplateContentRenderError` was thrown
[eleventy:dev] > (./src/index.njk)
[eleventy:dev] TypeError: Converting circular structure to JSON
Any and all insight is very much appreciated. Thanks :)

There's nothing inherently wrong with passing Eleventy collections through nested macros since a collection is just a regular JavaScript array. The issue arises depending on how you're using the collection inside Nunjucks since the collections object is a circular structure.
You can try this yourself by passing a collection into a macro and only accessing individual properties in the collection.
{# include.njk #}
{% macro navigation(data) %}
<ol>
{%- for page in data -%}
<li>
{{ page.data.title }}
</li>
{%- endfor -%}
</ol>
{% endmacro %}
{{ navigation(collections.all) }}
This setup works perfectly fine even with more macro nesting. The error you're running into comes from doing something like using the dump filter.
{# include.njk #}
{{ collections.all | dump }}
Since I don't know what your macros are doing, I'm not sure what is exactly causing the error to be thrown, but you might want to look for things that might be JSON.stringifying the collections object. Depending on your use case, you might have to manually parse the object yourself, find an external library, or only use the fields you need. Creating custom Eleventy filters may help.

Related

How can I catch properties of an object's relations in a twig dump?

I am exploring the possibilities of OctoberCMS in a test project about movies. Each movie has several properties, among others one or more genres (both movies and genres are models btw). I am now trying to code something for the front-end filters on a movie list page, but I am having problems trying to get to the field genre_title -see screenshot at https://i.stack.imgur.com/yrIHm.png. When I do {{ dump(movies.items[0].relations) }} I see the collection genres but from then on I am stuck. I have tried many different things:
{{dump(movies.items[0].relations[0].items)}}
{{dump(movies.items[0].relations['genres'].items)}}
{{dump(movies.items[0].relations.items)}}
etc.
Most of my attempts gave me NULL as a result. How can I get to the genre_title? I'd really appreciate it if someone could point me in the right direction! Many thanks!
Its collection so you can simply need to loop
{% for movie in movies %}
{% for genre in movie.genres %}
{{ genre.genre_title }} - {{ genre.slug }} - {{dump(genre)}}...
{% endfor %}
{% endfor %}
Now you can have all the data available.
if any doubt please comment.

Passing filename as variable to Jekyll include doesn't work

This works perfectly fine:
{% capture foo %}{% include test.html %}{% endcapture %}
I want to do this:
frontmatter.md:
---
baaz: test.html
layout: layout.html
---
layout.html:
{% capture foo %}{% include {{ page.baaz }} %}{% endcapture %}
But when I do I'm given this error:
"Liquid Exception: Invalid syntax for include tag. File contains invalid characters or sequences: Valid syntax: {% include file.ext param='value' param2='value' %}"
I've seen this addressed in several other questions, with the most recent explanation I've found being this:
"...dynamic filename paths can't be added due to the fact that the included files are calculated and added at the compilation phase and not at run time phase. And compilation phase means dynamic paths aren't yet recognized."
But that source is nearly two years old. Does anyone have a solution to this yet? Or a workaround that would allow me to include a file defined as a variable in frontmatter?
You can try {% include page.baaz %}
Edit : after some investigations, it appears that your syntax is correct, and that the error fires only when page.baaz is not present.
This ends up in an include tag which looks like this for liquid :
{% include %}
In order to avoid this error on certain pages/post with no baaz set, you can use a condition.
{% if page.baaz %}
{% capture foo %}{% include {{ page.baaz }} %}{% endcapture %}
{% endif %}
I just came to this case recently. I assume the syntax works as expected. See sample and result.
{% include {{ page.baaz }} %}
However in your case it might be the page name could not be put in a variable as the error stated:
Error: Invalid syntax for include tag:
File contains invalid characters or sequences
Valid syntax:
***% include file.ext param='value' param2='value' %***
So to come out from the problem I would suggest you to inventory all file names and choose it:
{% case page.baaz %}
{% when 'test.html' %}
{% capture foo %}{% include test.html %}{% endcapture %}
{% when 'othertest.html' %}
{% capture foo %}{% include othertest.html %}{% endcapture %}
{% else %}
This is not a test
{% endcase %}
I had a similar issue... I have found a very usable work-around. Allow me to share my experience and solution. I hope it helps you to find a suitable solution for your problem.
What I wanted to build
I wanted to make a page with multiple sections. The sections should be reusable, be able to contain includes and they should be easy to manage in the CloudCannon CMS.
What I came up with
I ended up using the following front matter:
---
title: Lorem ipsum
description: Lorem ipsum
image: /img/default.jpg
section_blocks:
- section: sectionwithinclude
- section: anothersection
- section: andyetanothersection
---
... and the following tempate:
{% for item in page.section_blocks %}
{% for section in site.sections %}
{% if item.section == section.slug %}
<div class="section {{ item.section }}">
{{ section.content }}
</div>
{% endif %}
{% endfor %}
{% endfor %}
Within the _sections folder/collection I have a file called sectionwithinclude.md that looks like this:
---
---
{% include mycustominclude.html %}
Why this is great
When you edit your page, CloudCannon will show the section_blocks as an array with reorder buttons. Additionally, CloudCannon will automagically recognize section as a collection and show the options in a dropdown. Therefore adding a section is a matter of adding an empty item to the array, selecting a section from the dropdown and reordering it with the array buttons. On the same time, the inline editing option of CloudCannon still works. So management of text can be WYSIWYG, while block management can be done in the front matter array.
Super easy and powerful for (you and) your editors.
PS. You might find out that you will have some 'scope' issues, because page no longer relates to the actual page, but to the section. To solve this you can/should alter the loop in the template. You can let the loop manage the include instead of the section.

Shopify Liquid conditionally include sections

So the current Shopify implementation of sections leaves a lot to be desired. The majority of the functionality is relegated to the homepage.
I'm trying to skirt around that to a certain degree but basically chucking all the section functionality (that would normally be split into multiple sections) into one section file, and then duplicating it for each product in the store, reusing the handle of each product as the section name.
E.g.: example-product-handle --> sections/example-product-handle.liquid
My idea was then to create, in the main product.liquid file, a simple routing system that would conditionally include a section if one exists that matches with the handle. This SO answer got my creative juices flowing.
The ideal result would look like...
{% assign current_page = product.handle %}
{% capture snippet_exists %}{% section current_page %}{% endcapture %}
{% unless snippet_exists contains "Liquid error" %}
{% section current_page %}
{% endunless %}
This works beautifully for snippets. Replace section with include in that code, and the routing system performs perfectly.
With sections however?
Liquid syntax error: Error in tag 'section' - Valid syntax: section '[type]'
Is there no way around this? Do section names have to be explicitly stated?
This isn't possible. It is purposefully not possible. Try instead using the section to dynamically include snippets.
{% for block in section.blocks %}
{% case block.type %}
{% when 'layout1' %}
{% include 'layout1' %}
{% endfor %}

Business Catalyst Liquid sorting

I can see that Liquid allows you to sort a collection using the below syntax:
{% assign sorted_items = items.all|sort:'Email' %}
{% for item in sorted_items %}
<div>Name: {{item.name}}</div>
<div>Email: {{item.email}}</div>
{% endfor %}
However this does not appear to work in Business Catalyst.
If I use this to render the result to the page it simply renders "null".
{{sorted_items | json }}
Should I be able to do this in Business Catalyst, or am I completely wasting my time trying to find a solution to sort my WebApp data?
You can sort the data like this:
{module_data resource="customers" version="v3" fields="firstName,email1" collection="myData"}
<pre>{{myData|json}}</pre>
{% capture emails -%}
{% for item in myData.items -%}
,{{ item.email1.value }} - {{ item.firstName }};
{% endfor %}
{% endcapture %}
<pre>{{ emails | split: "," | sort }}</pre>
The comma is not spelling mistake : )
After you split the string in array you can do whatever you need to do with it.
The answer from Daut is not good. Any solution in the for loop will only sort the number of items fetched from the module and the max amount for that is 500.
If you are using module_data you just use its actual sort!
{module_data resource="customers" version="v3" order="firstName" fields="firstName,email1" collection="myData"}
module_data supports both WHERE for filtering and ORDER to order the results.

Liquid templates - accessing members by name

I'm using Jekyll to create a new blog. It uses Liquid underneath.
Jekyll defines certain "variables": site, content, page, post and paginator. These "variables" have several "members". For instance, post.date will return the date of a post, while post.url will return its url.
My question is: can I access a variable's member using another variable as the member name?
See the following example:
{% if my_condition %}
{% assign name = 'date' %}
{% else %}
{% assign name = 'url' %}
{% endif %}
I have a variable called name which is either 'date' or 'url'.
How can I make the liquid equivalent of post[name] in ruby?
The only way I've found is using a for loop to iterate over all the pairs (key-value) of post. Beware! It is quite horrible:
{% for property in post %}
{% if property[0] == name %}
{{ property[1] }}
{% endif %}
{% endfor %}
Argh! I hope there is a better way.
Thanks.
I don't know what I was thinking.
post[name] is a perfectly valid liquid construction. So the for-if code above can be replaced by this:
{{ post[name] }}
I thought that I tried this, but apparently I didn't. D'oh!
Liquid admits even fancier constructs; the following one is syntactically correct, and will return the expected value if post, element, categories, etc are correctly defined:
{{ post[element.id].categories[1].name }}
I am greatly surprised with Liquid. Will definitively continue investigating.
Actually, this did not work for me. I tried a bunch of different combinations and what finally worked was
<!-- object myObj.test has the string value "this is a test" -->
{% assign x = 'test' %}
{{ myObj.[x] }}
Output:
this is a test

Resources