Liquid does not print object.field property - shopify

I'm new to Liquid and I'm trying to create a Mechanic script in Shopify to loop through all my orders and check each SKU. This is what I came up with:
{% for line_item in order.line_items %}
{% if line_item.sku %}
{{ line_item.sku }}
{% capture sku %}
On the third line, I was expecting to see it print an order's SKU, which should look like this: 1xSAMPLE1+2xSAMPLE2. It, however, showed me this error:
lexical error: invalid char in json text.
1xS
(right here) ------^
Typically, this comes down to a stray comma, or some other character that makes for invalid JSON.
Context (lines 2-5 of rendered JSON):
As I understand it, Mechanic (or Liquid) only wants to print JSON values. How do I view my variables while running the Mechanic script?
Here is the full code and the error log:

Related

How to work with product meta field in Shopify?

I have created a custom meta field for blog posts in Shopify. It's of the type product and I have chosen List of products.
I want to loop through the products and render them using the theme's product-grid-item template.
My problem is, that the type of my meta field output is a string - not an array.
If i try to output the field like this {{ article.metafields.blog_post.feature_products }}
I get the following:
["gid://shopify/Product/8078688583991","gid://shopify/Product/8078688715063","gid://shopify/Product/8078689141047"]
Which looks like an array, but liquid sees it as a string.
I have tried to loop through it like the following. But nothing happens
{% for product_id in article.metafields.blog_post.feature_products %}
{{ product_id }}
{% endfor %}
It feels like I'm missing something obvious. It doesn't make sense, that the output is a string.
Does anybody know how to output it like an array? Or how to convert the string to an array - without the quote-marks, brackets and so on.
For metafields that allow multiple values, the list of values is stored in the value property of the metafield. Add .value at the end of your loop statement.
Note that your product_id variable will be a product object. I suggest you rename it to "product". You can then access the id (and other properties) using the dot notation.
{% for product in article.metafields.blog_post.feature_products.value %}
{{ product.id }}
{% endfor %}

Liquid: Assigning and capturing existing custom attribute in the control flow code block

The purpose of this control flow block is checking to see if the existing level attribute is blank or an empty string, if it is, I would like to assign a default Level 1 to the code block. If the level attribute exists and is not blank, I would like to grab the current value of the customer's level. Somehow the Liquid engine is throwing an error in my code.
Can someone pinpoint where I've done wrong in my code? Thanks so much!
{% if customer.level == blank or customer.level =='' %}
{% capture customer.level %}Level 1{% endcapture %}
Hey! Your rewards level is: {{ customer.level }}
{% else %}
Customer level is: {{ customer.level }}
{% endif %}
In Shopify's Liquid, objects cannot be changed. Your attempt to assign a value to customer.level is failing because you are not allowed to overwrite, delete or add values in something like the customer object. (The . between customer and level indicates that "level" is a property of the "customer" object - customer.level does not represent a single variable on its own)
To fix the code, all you need to do is come up with a variable name that does not use the . character (or any other punctuation that can be interpreted by the code parser). For example, naming your variable customer_level (using an underscore) should do the trick for you.
Also note that level is not a property of a customer object in Shopfiy, and you cannot add arbitrary fields to the core object types. I would recommend storing your level as either a tag or a metafield on the customer if you aren't already. Note that assigning an appropriate tag or metafield cannot be done through the Liquid code, it has to be added by some app or process that has appropriate permissions.

Getting all products in shopify that do not contain a tag

I am using bundle builder app that lets users create their own bundles and purchase them. With each purchase it creates a product variation. When there are over 99 variations the app duplicates the bundle product and repeats itself till infinity. The problem here is that the old product bundles are no longer valid but still show in the front end causing confusion. I was able to bug bundle builder app support enough to provide me with info on how to detect these legacy products so we can hide these products from the collections page like so:
{%- for product in collection.products -%}
{%- if product.tags contains 'bundle-builder-dummy-legacy' -%}
** do nothing **
{%- else -%}
** print out product **
{%- endif -%}
{%- endfor -%}
Now this hides the legacy products but it still messes up the pagination and the page layout, for example our page limit is set to 8 products, we are on page 2 of 5. Using the above code snippet prints out only the products that do not contain the tag 'bundle-builder-dummy-legacy' (this could be improved with unless statement, but that is not the point), but it leaves empty space - only 6 grid items are filled instead of 8. So I guess the for loop is not working correctly. How can I get the products inside the for loop that do not contain the tag. Meaning an if/unless statement need to happen before the loop or during loop init. Hope I've made clear of the situation I have.
Thanks
What you might do is asking app to identify those products using a directly accessible attribute of the product, like type.
Then you would be able to do this:
{% assign collection_products = collection.products | where:'type','legacy' %}
{% for product in collection_products %}
Do something
{% endfor %}

Shopify(Liquid) multiple conditions in if statement

I am looping through all collections, and creating a preview item with each collection title, image and link. But I have 15 collections I would like to exclude.
Currently I am using 'contains' to exclude the 15 I don't want, but am wondering if theres a cleaner way to write this since its a really long if condition.
Thanks in advance!
Example below:
{% for collection in collections %}
{% if collection.title contains 'collection-1' or collection.title
contains 'collection-2' or collection.title contains 'collection-3'
or collection.title contains 'collection-4' or collection.title
contains 'collection-5' %}
{% else %}
// build item here
{% endif %}
{% endfor %}
I would create an array of exclusions and check to see if my exclusion array contains the collection in question. (And rather than the title, I would use the collection handle as the handle is guaranteed to only be 'clean' names and guaranteed to be unique)
Example:
{% assign collection_exclusion_array = 'collection-1, collection-2, collection-3, collection-4, collection-5' | remove: ' ' | split: ',' %}
{% for collection in collections %}
{% if collection_exclusion_array contains collection.handle %}
{% continue %}
{% endif %}
{% comment %} Build items here {% endcomment %}
{% endfor %}
How it works:
We cannot directly create arrays in Liquid - we can only make one by taking a string and using the split filter to create our array.
By using handles, we guarantee that our list values only contains letters, numbers and hyphens - there's no chance that our delimiter (in this case, the comma) can accidentally show up as part of the value.
We don't want spaces to be part of the array values, so we remove them before we use the split filter. We could instead just not put spaces between each value, but in my brain that reads like a terrible abuse of grammar. Either omitting spaces the first time or removing them after creating your string will work.
Now that we have our array of exclusions, when we loop through collections we can check to see if the current collection's handle shows up in the list.
If found, skip to the next collection using the continue statement - this saves a layer of indentation since we don't have to have an empty if followed by an else that contains everything that we want to do.
And there you go! Hope it helps :)
NB: For more information on handles in Shopify, see https://help.shopify.com/en/themes/liquid/basics/handle
An alternate method to achieve your exclusions:
If you give your collections some sort of flag that indicates that they shouldn't show up in your collection loop, you can manage each collection directly, rather than maintaining a separate list.
If we look at the collection page in your admin, though, we don't get a lot that's helpful: all we see are things like title, description, etc. Not even a place to give the collection a specific tag!
Fortunately, collections are able to have metafields - Shopify just has that feature hidden from normal users. Metafields allow you to create additional information for objects in your store (products, collections, pages, etc.), which you can then reference through Liquid.
You can read more about Shopify's use of metafields here: https://www.shopify.com/partners/blog/110057030-using-metafields-in-your-shopify-theme
My previous favourite plugin for accessing metafields was ShopifyFD, a browser extension that would let you view and edit that metadata right on your collection page, but unfortunately Shopify's recent changes to the admin have broken that plugin. The author is working on a new version, but it's not ready at the time of writing: https://freakdesign.com.au/blogs/news/shopifyfd-and-the-current-case-of-the-broken-tool
(Note: I haven't tried any of the other metafield-editing tools listed in the above linked article - when ShopifyFD started having trouble, I started doing my metafield editing using the admin API and creating/posting the requests myself: https://help.shopify.com/en/api/reference/metafield)
Once you have a way to easily set metafields (which, surprisingly, seems to be the hard part right now), your for-loop logic is extremely simple. Let's assume that the metafield you create for this purpose has the namespace 'preview' and the key 'exclude':
{% for collection in collections %}
{% if collection.metafields.preview.exclude %}
{% continue %}
{% endif %}
{% comment %} Do stuff! {% endcomment %}
{% endfor %}
This will now skip any collection that has any value set in your custom field, so if you change your mind about any current or future collection all that needs to change is the one metafield on the collection itself.

Jinja / Django for loop range not working

I'm building a django template to duplicate images based on an argument passed from the view; the template then uses Jinja2 in a for loop to duplicate the image.
BUT, I can only get this to work by passing a list I make in the view. If I try to use the jinja range, I get an error ("Could not parse the remainder: ...").
Reading this link, I swear I'm using the right syntax.
template
{% for i in range(variable) %}
<img src=...>
{% endfor %}
I checked the variable I was passing in; it's type int. Heck, I even tried to get rid of the variable (for testing) and tried using a hard-coded number:
{% for i in range(5) %}
<img src=...>
{% endfor %}
I get the following error:
Could not parse the remainder: '(5)' from 'range(5)'
If I pass to the template a list in the arguments dictionary (and use the list in place of the range statement), it works; the image is repeated however many times I want.
What am I missing? The docs on Jinja (for loop and range) and the previous link all tell me that this should work with range and a variable.
Soooo.... based on Franndy's comment that this isn't automatically supported by Django, and following their link, which leads to this link, I found how to write your own filter.
Inside views.py:
from django.template.defaulttags import register
#register.filter
def get_range(value):
return range(value)
Then, inside template:
{% for i in variable|get_range %}
<img src=...>
{% endfor %}