I have the following string:
“Title
Content
Title
Content”
I want to extract each title and each content.
I have been trying to use Shopify remove operator but it’s getting really long and I’m not sure if there is a easier solution?
Using split filter might be the answer here!
However for this use case to work you need to add custom separators between each title/content block, if you do have access to the content, it would look something like this:
<!-- Let's assume this content is coming from page.content -->
<h2>Title 1</h2><!-- inner-split --><p>content 1</p>
<!-- outer-split -->
<h2>Title 2</h2><!-- inner-split --><p>content 2</p>
<!-- this split the content into title/content blocks -->
{% assign content_blocks = page.content | split: '<!-- outer-split -->' %}
<!-- Loop through all blocks to split then extract title/content -->
{% for block in content_blocks %}
{% assign title_content = block | split: '<!-- inner-split -->' %}
<!-- title -->
{{ title_content[0] }}
<!-- content -->
{{ title_content[1] }}
{% endfor %}
Related
On a Shopify product page, I can loop through all the variants and get information about them like this:
{% for variants in product.variants %}
{{ variants.id }}<br>
{{ variants.sku }}<br>
{{ variants.title}}<br>
{{ variants.price | divided_by: 100}}<br>
{% endfor %}
This works perfectly fine, however when I try and get the variant image in the loop like this:
{{ variants.image}}
I have also tried using:
{{ variants.featured_image.src}}
Which produces exactly the same result.
The output is simpy products and then the name of the image file. For example 'products/my-image.jpg'.
This is obviously not the path to the image and when I try and display this within an image tag, a broken image is produced.
I am trying to get the path to the image that is stored on Shopify's CDN for this variant product, which will look something like https://cdn.shopify.com/s/files/1/0022/4572/2212/products/WI1M1L1F1B1_grande.jpg?v=1656698226.
Does anyone know how this can be achieved within the loop I am using above?
You are looking for this
{{ variant.image.src | product_img_url: '200x' }}
I suggest you to take a look here to find many other useful functions: https://www.shopify.ca/partners/shopify-cheat-sheet
Also is useful to download a free theme (Dawn for example) and use it as reference.
Here is a code example to show each variant image and url :
<div class="product_thumbnails">
{% for variant in product.variants %}
<a href="{{ variant.url }}">
<div class="h-[50px] w-[50px] bg-[#E6ECEB] rounded shrink-0 p-1">
{{ variant.image | image_url: width: 50 | image_tag }}
</div>
</a>
{% endfor %}
</div>
What I want to do:
Capture two variables {{section.settings.{{title}}_modal_title}}
Context:
The links in the linklist are not really used as links, but purely so user can reorganize it in their Shopify portal.
External javascript file creates elements (modals) on click event, value from section.settings.XXX_modal_title is supposed to be used in the created modals.
The linklist:
<ul class="menu">
{% for link in linklists.main-menu.links %}
<li data-menu="{{ link.title | upcase }}" class="menu-link">
<p class="menu-title">[{{ link.title | upcase }}]</p> <-- Click event
</li>
{% endfor %}
</ul>
What I tried:
My first try was adding the liquid tag itself with .insertAdjacentHTML('afterbegin', {{section.settings.${attLowerCase}_modal_title}}) when the element is created, but soon realized that liquid renders the values first. The output of doing this is literally "{{ }}"
My second try was to capture two variables and put it in a hidden input.
{% assign title = link.title | downcase %}
{% capture section_title_variable %}
{{section.settings.{{title}}_modal_settings}}
{% endcapture %}
<hr class="item-line">
<p class="ueq-menu-title">[{{ link.title | upcase }}]</p>
<input type="hidden" id="about_modal_title" value="{{section_title_variable}}">
In depth example:
section.settings
{
"type": "text",
"id": "about_modal_title",
"label": "About window title",
"default": "ABOUT"
},
User clicks on link ABOUT
<ul class="menu">
{% for link in linklists.ueq-menu.links %}
<li data-menu="{{ link.title | upcase }}" class="menu-link"> <-- "ABOUT"
{% assign title = link.title | downcase %} <-- downcase to "about"
{% capture section_title_variable %}
{{section.settings.{{title}}_modal_title}}
{% endcapture %}
<hr class="item-line">
<p class="menu-title">[{{ link.title | upcase }}]</p>
<input type="hidden" id="about_modal_title" value="{{section_title_variable}}"> <-- captured should look like this {{section.settings.about_modal_title}}
</li>
{% endfor %}
</ul>
on click event, get the value from hidden input, and use it when creating elements.
"Why not use {% javascript %}?"
I prefer to do it this way, since most of my script files are external. I like to keep it that way.
"Why capture it like that?"
The links in the linklist are already known, this was in the design. Thats why in sechema one of them is called "about_modal_title". Since this is used inside a for loop, this was the only way I could come up with to connect the title and schema settings with each other.
If someone knows a better way to do this instead of putting it in a hidden input, please let me know :)
In proper Liquid syntax, curly-brackets are never embedded inside curly-brackets
The syntax {{ something_{{ you }}_want }} is illegal Liquid syntax - once an expression is started, using either {{ or {%, everything up to the next }} or %} is evaluated as one template command.
In your example, you're looking to combine two strings in order to get the name of a setting that you want to access. In a dedicated programming language, we would expect there to be nice shortcuts for something like this. Liquid, however, is first and foremost a templating language, and doesn't have such shortcuts. Instead, we will need to accomplish our goal in two steps.
The first thing we need to know is that we can access properties in two ways: {{ settings.fieldname }} is equivalent to {{ settings['fieldname'] }}
Thanks to that second syntax, we can access an arbitrary setting using a variable:
{% assign field = 'fieldname' %}
{{ settings[fieldname] }}
So to do something more complicated, we just need to use assign (or capture - just be aware that capture includes whitespace as well!) to get a variable that contains the name of the field we want to access, then pass that in to the square brackets:
{% assign title_field = link.title | downcase | append: '_modal_title' %}
{{ section.settings[title_field] }}
I rely on template inheritance system to insert extra_css and/or extra_js into my pages:
base.html:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Core CSS -->
{% block extra_css %}
{# Override this in templates to add extra stylesheets #}
{% endblock %}
</head>
<body>
{% block content %}{% endblock content %}
<!-- Core JS -->
{% block extra_js %}
{# Override this in templates to add extra javascript #}
{% endblock extra_js %}
</body>
</html>
page.html:
{% extends "base.html" %}
{% block extra_css %}
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/css/select2.min.css" rel="stylesheet" />
{% endblock %}
{% load wagtailcore_tags wagtailimages_tags %}
{% block content %}
<div class="blog-post">
<!-- Custom HTML -->
{% block content %}
{% include_block page.body %}
{% endblock %}
</div><!-- /.blog-post -->
{% endblock %}
{% block extra_js %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/js/select2.min.js"></script>
<script type="text/javascript">
// Custom JavaScript
</script>
{% endblock extra_js %}
This works great, thus extra css/js is indeed inserted where it belongs.
The problem arise if I need to use streamfield where one of its block templates need custom css/js. In this case custom resources are inserted along with the block, but not in the specified locations in the base.html.
For instance if extra_js from the page.html in the example above has been added to the block template instead, then select2 would complain about jquery not being present and it would be totally right, because it's inserted with the block, but not as intended after the Core JS libraries.
Loading jquery twice leads to other issues: https://stackoverflow.com/a/25782679/2146346
Other option is to load all block dependencies into the page, but it would fill pages with redundant resources as not every block from the streamfield might be used on the page.
Are there other options?
UPDATE: here’s a much better way that uses JavaScript to only load libraries once: https://github.com/FlipperPA/wagtailcodeblock/blob/main/wagtailcodeblock/templates/wagtailcodeblock/code_block.html#L6
I haven't come up with a way I like of doing this yet. The tools for doing this on the Wagtail editor side are nice. However, here's what I do for WagtailCodeBlock:
{% load static wagtailcodeblock_tags %}
{% spaceless %}
{# This is ugly, as it'll inject this code for each block, but browsers are smart enough to not load each time. #}
<script src="{% static 'wagtailcodeblock/js/prism.min.js' %}" type='text/javascript'></script>
<link href="{% static 'wagtailcodeblock/css/prism.min.css' %}" rel="stylesheet">
{% load_prism_theme %}
{% for key, val in self.items %}
{% if key == "language" %}
<script>
language_class_name = 'language-{{ val }}';
</script>
{% endif %}
{% if key == "code" %}
<pre class="line-numbers">
<code id="target-element-current">{{ val }}</code>
</pre>
<script>
var block_num = (typeof block_num === 'undefined') ? 0 : block_num;
block_num++;
document.getElementById('target-element-current').className = language_class_name;
document.getElementById('target-element-current').id = 'target-element-' + block_num;
</script>
{% endif %}
{% endfor %}
{% endspaceless %}
In this example, I load the JS/CSS assets in each, and let the browser settle it out. It also assumed jQuery is loaded at the parent level. However, it is also possible to use the JavaScript context to ensure things are only loaded once, which is my next step. For now, it isn't pretty but it works.
On the Wagtail editor side, there's the #property media. I wish there was something analogous on the rendered side:
class CodeBlock(StructBlock):
"""
Code Highlighting Block
"""
WCB_LANGUAGES = get_language_choices()
off_languages = ['html', 'mathml', 'svg', 'xml']
language = ChoiceBlock(choices=WCB_LANGUAGES, help_text=_('Coding language'), label=_('Language'))
code = TextBlock(label=_('Code'))
#property
def media(self):
theme = get_theme()
prism_version = get_prism_version()
if theme:
prism_theme = '-{}'.format(theme)
else:
prism_theme = ""
js_list = [
"https://cdnjs.cloudflare.com/ajax/libs/prism/{}/prism.min.js".format(
prism_version,
),
]
for lang_code, lang_name in self.WCB_LANGUAGES:
# Review: https://github.com/PrismJS/prism/blob/gh-pages/prism.js#L602
if lang_code not in self.off_languages:
js_list.append(
"https://cdnjs.cloudflare.com/ajax/libs/prism/{}/components/prism-{}.min.js".format(
prism_version,
lang_code,
)
)
return Media(
js=js_list,
css={
'all': [
"https://cdnjs.cloudflare.com/ajax/libs/prism/{}/themes/prism{}.min.css".format(
prism_version, prism_theme
),
]
}
)
class Meta:
icon = 'code'
template = 'wagtailcodeblock/code_block.html'
form_classname = 'code-block struct-block'
form_template = 'wagtailcodeblock/code_block_form.html'
I hope this gives you some ideas, and I'm all ears if you come up with a better way. Good luck.
Could you create another js block that you only add into the streamfield block template? So in base.html you'll have extra_js block as well as streamblock_js. You can have your jquery in extra_js and the extra dependency for the streamblock in streamblock_js. And if you have multiple custom css/js per streamblock on one page, you could add as many extra blocks in the base.html template to load all the dependencies. I'm not sure if this will work, but that's my idea.
I’m trying to add a link to a size guide image by having it pull the last image from a collection.
but right now it’s pulling all the images, not the last. how do i just pull the last?
<!-- SIZE GUIDE -->
<div id=“size_guide”>
{% for image in product.images %}
<a href=“{{ image.src | product_img_url: ‘master’ }}” class=“fancybox-media {% cycle ‘,last’ %}”>
SIZE GUIDE</a> {% endfor %}
</div>
</div>
Try using the last array filter. E.g:
SIZE GUIDE
On my product page, with the following line of code in the product.liquid template, it generates 3 seperate dropdowns for the 3 different product options the product has, eg Type Color Size..
<div class="select">
<select id="product-select" name="id">
{% for variant in product.variants %}
<option {% if variant == product.selected_or_first_available_variant %}selected="selected"{% endif %} value="{{ variant.id }}">{{ variant.title }}</option>
{% endfor %}
</select>
</div>
however, i used the same code in the product-loop.liquid to show the same drop down on the collections page (without the option to add to cart).. but this time the dropdown appears as 1 single combined dropdown.
how do i split them up like before?
heres a screenshot of what both dropdowns look like :
You need to include option_selection.js in your collection.liquid template.
paste the following line at top or bottom of your collection.liquid template
{{ 'option_selection.js' | shopify_asset_url | script_tag }}