Did liquid array accessors in shopify templates change recently? - shopify

For some reason this bit of liquid just stopped working in a site I did some months back:
{% assign packs = collections.packs.all_products %}
{{ packs[0].title }}
Accessing the array items in a for loop still works, but I am unable to access the items directly using an index value. Is this a recent change to the template system or is something else going on?

this should work again. A change in liquid that caused this was reverted last week.

Related

Liquid - Load all products from collection handler not working

I am trying to load all products by the given handler from a collection.
They will click on a box (each box is a different collection), then it should load all products for that collection.
My issue is that my assigned variable cannot read the input from javascript.
I am doing onclick(id, name, handle) where i catch the handle and pass it to the liquid.
My code is:
function loadProducts(collectionH) {
var html = '';
var handlerString = collectionH;
console.log('Loading products...');
{{collectionHandleNew}} = handlerString;
console.log({{collectionHandleNew}}); // log the handler
// Make sure the current product name is loaded
{% if collectionHandleNew -%}
{% if selectedCategory -%}
console.log('Collection handle is set: ' + {{collectionHandleNew}});
{%- for product in collections[collectionHandleNew].products -%}
console.log({{product.id}});
{%- endfor -%}
{% else -%}
console.log('Selected category not found');
{% endif -%}
{% else -%}
console.log('Collection handle is not available');
{% endif -%}
return html;
}
The console is showing this:
view the image
The trick here is to remember that Liquid and Javascript serve two completely different purposes:
Liquid is a templating language that is parsed server-side to generate the documents that are sent to the client's browser, and is never seen by the client.
Javascript is a programming language that is parsed client-side to do dynamic things on the page and is never executed on the server.
Why is this distinction important? Because it means that we can use Liquid to generate Javascript, but the reverse is never true. It also means that any variables that we pass from Liquid to Javascript will only be current as of the time the document is generated and cannot be affected by anything that happens on the page once it's been rendered!
If your code needs to fetch a collection dynamically, I would recommend creating a function that takes a collection handle and loads the products entirely through Javascript using your favourite tool (fetch, jQuery.getJSON, XMLHttpRequest, Axios, etc).
To fetch products from a collection from the storefront, you can use /collections/<some-collection-handle>/products.json (for example, /collections/drawstring-bag-hoodies/products.json). If you're feeling fancy, you might also consider looking into the storefront GraphQL API instead of the traditional REST API.
One final note: Whenever you are dumping variables from Liquid into Javascript, I strongly recommend using the | json Liquid filter to ensure that the resulting output is Javascript-legal. There are lots of ways a Liquid variable dump can break Javascript, such as when the variable is unexpectedly empty, when it contains ' or ", when it contains line breaks, etc. By running the Liquid variable through this filter, the resulting output will be wrapped in the appropriate brackets or quotes, all special characters within will be properly escaped, and empty values will print null.

How To Enable Block Inside Fifty Different Sections Without Copying Meta Code?

I am developing a Shopify Theme. The structure I have now is (excerpt):
Snippets
custom-message-snippet
Sections
custom-message-section
welcome-page-a-section
welcome-page-b-section
Templates
welcome-page-a-template
welcome-page-b-template
...
Custom message snippet uses settings that are in the custom-message-section, that is:
a) message
b) header text
I'd like for users to be able to add custom-message-snippet to welcome-page-a and welcome-page-b in a way that settings for both are different.
I can not render section 'custom-message-section" inside welcome pages because it is not possible (and a workaround is nasty).
There are fifty welcome pages. Every welcome page is totally different.
My question is:
How to allow users to use custom-message-snippet in all welcome pages without copying and pasting custom-message-page setting schema to each and every welcome page?
combine custom-message-section to custom-message-snippet
combine welcome-page-\w+-template to welcome-page-template, you only need to have one template and schema set, the user will control the sections in schema selection.
in template, use schema to select the page-content; this, defined by a bunch of if-else /switch statement.
{% section "welcome-page-custom-message-section" %}
{% section "welcome-page-content" %}
in welcome-page-custom-message-section
{% render "custom-message-snippet" with "string from welcome-page-section schema " %}
create checkbox select schema for custom message to appear or not.
Short: You can't.
Long: Using storefront 2.0, you end you having blocks, which can be added manually to any template.
It is possible if you have a little knowledge of Node.js and you're using the Shopify CLI.
Check the solution here: Shopify Section / Block Schema In A Separate File. It is possible 

How do I add a metafield to liquid in Shopify?

I'm trying to edit a section in my Shopify theme which displays a video. I'm using the section on product pages and the URL for the video is inserted by using the theme editor tool. As such the same video inserted will show on every product page using that template. However, I would like to use a different video on each product page. I can't see a way to do this through the theme editor unless I duplicate the template for each and every product that needs its own video URL (seems a bit overkill).
I found this code which controls the video section, and the bit I'm trying to figure out is how I can change the 'assign video_id' to use a metafield so that I can add this per product by simply adding the video URL on a specific product page. I can't think of an easier way to achieve what I need, but thought changing a metafield for each product that needs a custom video URL would be the best way to do it, working possibly similar to this:
{% if template.name == 'product' %}
{% if product.metafields.my_fields.product_video_url %}
{{ product.metafields.my_fields.product_video_url }}
{% endif %}
{% endif %}
If anyone had an idea of how I can do this using the code below and inserting a metafield (and even better, a metafield IF it's filled, otherwise default back to video_url.id) I would really appreciate it.
{%- liquid
assign bg_color = section.settings.background-color
assign button_text = section.settings.button-text | escape | truncate: 30
assign button_url = section.settings.button-url | url_escape
assign full_width = section.settings.full-width
assign heading = section.settings.heading | escape
assign darken_video = section.settings.darken-video
assign light_text = section.settings.light-text
assign section_height = section.settings.section-height
assign sub_heading = section.settings.sub-heading | escape
assign thumbnail = section.settings.image
assign video_url = section.settings.video-url
if video_url.id
assign video_id = video_url.id
else
assign video_id = '_9VUPq3SxOc'
endif
assign cover_link = false
if button_text == blank and button_url != blank
assign cover_link = true
endif
assign button_type = 'button'
if light_text
assign button_type = 'inverted-secondary-button'
endif
-%}
Thank you for any help at all.
Metafields are pretty simple, and very useful. First off, you can set them in the Admin. Create one with a description.
Note the concept of my_fields is really dumb, instead, think of it as "context", the "what am I in all this". So you might use a namespace here, like "product_videos". Hence, at this point, think of it as a box named, with whatever is inside the box, still a mystery!
product.metafields.product_videos
Or your company name. Or your cat's name. Or whatever turns your crank. my_fields is just generic filler. Means nothing.
Once you get over that hump, that you've now got a namespace, you can actually get down to the nitty-gritty. Anything of value to you is likely to be a string here, a string representing a video. So you want to make the type of the Metafield to be text. A string of text.
To be of much use, you need a key. You want to grab onto the key! So if a product has a special movie, let the key guide you right to it! The key is going to be something brilliant like; video_id! So now you have a product, with a Metafield resource, in your namespace, product_videos, with a key! Namely video_id. And you can then go to that product in your Shopify Admin, and at the bottom of the details page, fill in the answer to the question of video_id. Give it some info. Where to find the video. What it is called. Anything useful to you.
Now in your theme, just reference {{ product.metafields.product_videos.video_id }} when you need it.

How can I fix this 'Liquid Warning: Liquid syntax error: Expected id but found open_square in'

I just started running my blog using Github pages.
I chose 'minimal mistakes' theme and encountered a problem while customizing.
{% assign posts = site.categories.['Effective Java'] %}
The reason why I used square bracket is because of the blank between 'Effective' and 'Java'.
It actually works well without any problems, but I don't want to see the Liquid Warning any more.
Is there a better way to deal with the blank?

Django SQL query for each include/extends

I'm using Django to realize a engineering management system. I've made something really wrong somewhere, and my SQL query count is very high on some pages.
For example, I got 95 to 98 SQL queries in a single page, a simple ListView. All the queries are the same :
SELECT * FROM "syncoor_codification" LIMIT 21
They always return the same object. I suspect the queries to be triggered by my model's get_queryset() function.
If I use Django Debug Toolbar, I can see that the queries are triggered inside the template, by lines like :
{% extends 'syncoor/base.html' %}
{% extends 'syncoor/docs/base.html' %}
{% extends 'syncoor/docs/codifications/base.html' %}
{% include 'syncoor/js/jsp.js' %}
How could I get rid of this extra overhead ?
Edit : here's a screenshot :
In the picture you posted, the stack trace shows 2 things of note.
The SQL calls are being performed inside a signal handler associated with django-debug-toolbar, specifically the TEMPLATE panel.
The SQL call has it's limit clause set to REPR_OUTPUT_SIZE, Indicating that the template panel is trying to render a representation of a queryset.
This leads me to believe that you have passed a function or lazy object that returns a new queryset into the context at some point and it is this object that is being evaluated by the template panel. See if you can spot a bunch of Entity Objects in the context in the templates panel.
To confirm, try setting your settings like this.
DEBUG_TOOLBAR_PANELS = (
'debug_toolbar.panels.version.VersionDebugPanel',
'debug_toolbar.panels.timer.TimerDebugPanel',
'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel',
'debug_toolbar.panels.headers.HeaderDebugPanel',
'debug_toolbar.panels.request_vars.RequestVarsDebugPanel',
# 'debug_toolbar.panels.template.TemplateDebugPanel',
'debug_toolbar.panels.sql.SQLDebugPanel',
'debug_toolbar.panels.signals.SignalDebugPanel',
'debug_toolbar.panels.logger.LoggingPanel',
)
It looks like there are loops running in your base.html templates that are hitting the database. That's why inheriting from those templates is generating queries. Without seeing the templates themselves it's hard to say what the exact problem is, but that's certainly where you'll find the answer.