Variable scope in Shopify Sections - shopify-template

I'm trying to use a variable in my section file but it doesn't appear to be inherited from it's parent template.
For example:
index.liquid
{% assign foo = "bar" %}
{% section 'example' %}
sections/example.liquid
<h1>{{ foo }}</h1>
{% schema %}
{
"name": "Example",
"settings": [
...
]
}
{% endschema %}
It will not output the value of {{ foo }}, instead I just get: <h1></h1> as if the variable was never defined.
I thought sections would work like snippets, where anything defined in the parent template would be in scope in the included snippet:
index.liquid
{% assign foo = "bar" %}
{% include 'example' %}
snippets/example.liquid
<h1>{{ foo }}</h1>
Where I would get <h1>bar</h1> when rendered.
Is this a bug, or intended behaviour?
Is there a way I can include a section and use variable from some form of outer-scope?
Thanks!

If this is intended behaviour, I managed to find a way round it and thought I would post my not-perfect but workable solution:
sections/example.liquid
<h1><!-- foo --></h1>
You can use capture, to get the contents of the section as a string and use string filters on the captured markup:
index.liquid
{% assign foo = "bar" %}
{% capture section %}{% section 'example' %}{% endcapture %}
{{ section | replace: '<!-- foo -->', foo }}
You could of course replace any string, with your variable. But I found HTML comments work well because if you forget to run the replace, or don't need to - nothing is rendered.
If you wanted to do something more complex, like remove some markup from the section you could:
sections/example.liquid
<div>
<!-- REMOVE_TITLE? -->
<h1>{{ section.settings.title }}</h1>
<!-- REMOVE_TITLE? -->
<ul>
{% for block in section.blocks %}
<li>{{ block.settings.image | img_url: '300x' | img_tag }}</li>
{% endfor %}
</ul>
</div>
Then you could do something like:
{% capture section %}{% section 'example' %}{% endcapture %}
{% assign parts = section | split: '<!-- REMOVE_TITLE? -->' %}
{% for part in parts %}
{% assign mod = forloop.index | modulo: 2 %}
{% if mod > 0 %}{{ part }}{% endif %}
{% endfor %}

I would assign all your variables in a snippet and the keep including this same snippet in whichever scope you need to use the variables in....
That is a fairly DRY approach.
Also anything defined in config/settings_schema.json has a global scope but it can be given new values in theme settings by end users.

Related

How to create a dropdown in Twig using the syntax in the manual?

I'm trying to follow the syntax in the docs: https://github.com/craftcms/contact-form/blob/v2/README.md to output a select. I tried all sorts of syntax but I couldn't get it right… 😅
From the docs, I would have thought this would work, but it just adds the options as an attribute of the select field in the HTML.
{{
tag(
'select',
{
id: 'type',
name: 'message[type]',
options: [
{
label: 'option1',
value: 'option1'
}
],
class: message and message.hasErrors('message.type')
? 'error'
}
)
}}
The HTML output I get from that:
<select id="type" class="" name="message[type]" options="[{...;}]"></select>
I know I could just code it up as "html" but I'd like the keep the markup consistent and it makes the validation cleaner. Any pointer in the right direction much appreciated!
What I think you want.
I understand that you want to produce a dropdown selection using the tag() function, and you want to avoid just slapping in some HTML and calling it a day.
The HTML we want out of this...
A <select> tags options should be inside the element, as <option>s.
So the output we want is.
<select id="type" class="" name="message[type]">
<option value="option1">Option1</option>
<option value="option2">Option2</option>
</select>
tag() function vs the {% tag %} tag
We have two options for making a tag in Craft-Twig. The tag() function and the {% tag %} tag.
While they give use two routes to the same end...
tag() is better for when the tag has no innerHTML/innerText, or the contents are pulled from another function or API.
{% tag %} is better for when the tag has longer content, or that content is being dynamically generated.
I think {% tag %} is the better option for this situation, but I'll go through them both.
The tag() function
Documentation: Craft CMS - Functions - tag()
tag('tag_type',{options_object})
The tag_type is the <tag_type>. Everything else is optional.
The options_block may include two options that affect the inner contents of a tag:
text: "Woo!": Text will be HTML encoded and rendered inside your tag.
html: "<i>Yay!</i>": HTML to be slapped into your tag, with no safety-net.
Everything else is stringified and added as an attribute.
So id: "thinger becomes <tag id="thinger">
Why is your code doing this?
option={...} isn't one of tag()'s two 'inner stuff' options, so it's just turned into a string and slapped in as an attribute.
To get what you want with tag()
Just add your desired innerHtml as a string to the options_object's html key.
{{
tag(
'select',
{
html:"<option value="Option1">Option1</option><option value="Option2">Option2</option>"
}
)
}}
As you can see, though, that can be a bit cumbersome when you have long HTML to insert.
The {% tag %} tag
Documentation Craft CMS - tags -tag
{% tag %} works almost exactly like tag(), except it let's us put the 's contents inside {% tag %} and {% endtag %}.
It accepts one argument, the tag type ('element' below), and an optional object via with, with each key/value pair becoming attributes on the tag to create.
{% tag 'element' with {
class: "some class"
custom_attribute: "some value"
}
%}
Your html {{ 'and_Twig()'|upper }} here.
{% endtag %}
becomes
<element class="some class" custom_attribute="some value">Your html AND TWIG here.</element>
This is better suited for when you have verbose tag contents, or contents that are dynamically generated by other tags/functions.
To get what you want with {% tag %}
Just put your option tags inside the {% tag %}...{% endtag %}
{% tag 'select' with {
id: 'type',
name: 'message[type]',
-%}
<option value="option1">Option1</option>
<option value="option2">Option2</option>
{%- endtag %}
But I don't want to HTML it up...
No problem!
{% tag 'select' with {
id: 'type',
name: 'message[type]',
-%}
{% tag('option', {
text: "Option1"
value: "option1"
}) %}
{% tag('option', {
text: "Option2"
value: "option2"
}) %}
{%- endtag %}

How do I get my Eleventy template to not encode HTML tags?

I have an .md file which iterates over a collection of tags:
---
title: The First Page
date: Created
tags:
- home
- flashcards
- info
- other
---
## {{ title }}
**Publish Date:** {{ page.date }}
This is the index page now.
<ul>
{% for item in tags %}
<li>{{ item }}</li>
{% endfor %}
</ul>
But when I run it (eleventy --serve), it encodes the HTML tags:
How do I get it to not encode the HTML tags?
Your tags are being rendered as a code block. Inspect the element and you'll notice that they are rendered as text inside a <code> tag, which is subsequently inside a <pre> tag.
This is happening because you have indented your <li> lines with four spaces, which markdown-it (Eleventy's default markdown parser) treats as a code block, as that is CommonMark's spec.
You have two ways you can solve this. One is to keep your code the same and use a smaller number of spaces for indentation:
<ul>
{% for item in tags %}
<li>{{ item }}</li>
{% endfor %}
</ul>
But this is a markdown file, so you could simplify it further by writing markdown.
{% for item in tags %}
- {{ item }}
{% endfor %}

Octobercms - render fields from repeater groups on theme settings

I try to use repeater groups on my theme settings yaml file. So i add the above code to my theme/config/fields.yaml:
fields:
cont:
tab: Content
name: cont
label: Content
type: text
content:
tab: Content
label: Content
prompt: Add content block
span: full
type: repeater
groups:
textarea:
name: Textarea
description: Basic text field
icon: icon-file-text-o
fields:
text_area:
label: Text Content
type: textarea
size: large
quote:
name: Quote
description: Quote item
icon: icon-quote-right
fields:
quote_position:
span: auto
label: Quote Position
type: radio
options:
left: Left
center: Center
right: Right
quote_content:
span: auto
label: Details
type: textarea
Everything is working fine on theme settings backend and i can insert data on my fields.
Now i try to render those fields on my cms page but no mater what i try i never succeed. I try:
{% for fields in this.theme.content%}
{{ fields.textarea }}
{% endfor %}
also
{% for fields in this.theme.contents %}
{% if fields.groups == "textarea" %}
{{fields.groups.textarea}}
{% endif %}
{% endfor %}
But i can't render fields.
Hmm, it seems some confusion there and some wrong variable names :)
let's fix this.
End result would be like this :
{% for field in this.theme.content %}
{% if field._group == "textarea" %}
<h1>{{field.text_area}}</h1>
{% endif %}
{% if field._group == "quote" %}
<h1>{{field.quote_position}}</h1>
<h1>{{field.quote_content}}</h1>
{% endif %}
{% endfor %}
what are the mistakes [ If you are in hurry skip this :) ] (its here for your improvement it does not server any other purpose :) )
You are using contentso you need to make sure you use correct variable name here you can use this.theme.content not the this.theme.>>contents<<
next its field._group not the fields.groups
and finally fields its
fields:
text_area:
....
so you need to use them field.text_area not the field.textarea and field.quote_content so on ...
if you find any difficulties please comment.

Assign variable from filter within template

Im attempting to assign a variable returned from a filter to a variable inside a template. Is this even possible?
My filter:
def return_an_object(input)
Model.find(input)
end
The liquid:
{% assign bar = {{ "foo" | return_an_object }} %}
This results in input being nil within return_an_object.
Is it at all possible to do something like this?
Answering this myself.
{% assign bar = {{ "foo" | return_an_object }} %}
Should be:
{% assign bar = "foo" | return_an_object %}

how to include a partial .erb in a liquid template?

I'm trying to someting like this
{% for movie in movies%}
*{{ movie.name }}* {{ movie.price | currency }}
Ratin: {{ movie.rating }}
{% endfor %}
{% render_erb 'partial/footer' %}
footer would be a file like footer.erb.html.
How can I include as a partial as a erb file?
the solution is very easy:
{% template 'common/footer' %}
when you call render, the file extension is used to chose which template engine to use. So a regular call to render should do the trick, given that your partial has the right extension. I say should because I didn't try, but I don't see why it wouldn't work.

Resources