Shopify: Why does Liquid sometimes use {%- instead of {%? - shopify

I've been looking through the code of the default theme, and I've noticed that sometimes they use the {% tag to indicate Liquid code (as per the documentation), but other times they use {%-.
For example:
{% case section.settings.image_size %}
{% when 'small' %}
{%- assign product_image_width = 'medium-up--one-third' -%}
{%- assign product_description_width = 'medium-up--two-thirds' -%}
I cannot find a single example of {%- in the Liquid documentation, either on the Shopify site, or on GitHub, but I've seen other people use it on the Shopify forums, too.
What does the addition of a - signify?

The hyphens are a new syntax option that you can use to suppress blank lines that would otherwise show up in the source. You can learn more about this here: https://shopify.github.io/liquid/basics/whitespace/

Related

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.

Listing all products that contain current tag (on product page)

I've tried searching but couldn't figure this out.
I'm wondering if it's possible to list all products that use the same tag as the current product.
For instance, if I'm on a product page that uses the tag "red", I'd like to list all of the other products that also use that tag.
Is this possible and if so how would I accomplish this?
There are some ways but I'm not sure if these will do you any good.
1) AJAX
The easiest ways is to make an AJAX request to the /collections/all/TAG and get the required products and append them.
2) Paginate hack
{% paginate collection.all.products by 1000 %}
{%- for _product in collection.all.products -%}
{%- if _product.tags contains 'red' -%}
list the product
{%- endif -%}
{%- endfor -%}
{% endpaginate %}
Please note that this may increase the load time drastically depending how many products you have.

Displaying Collection thumbnails by matching Variant

One of the Collections on the Shopify theme that I'm editing uses the settings: Products must match - Variant's Title contains Red
How would I go about tweaking the collection-loop.liquid template (or other snippets?) to have this collection use the relevant variant product images associated with Red as the thumbnails, without messing up the other collections?
A: Make an alternate template and apply it to the collection(s) in question
Shopify allows you to make multiple templates for each of the main types of pages and set which one you want to use in the admin.
Step 1: Open the theme editor for your live theme
Go to [your-shopify-store].myshopify.com/admin/themes
The top half of the admin screen should be a showcase of your live theme. Click 'Actions' (right above the main preview) and select 'Edit HTML/CSS'
Step 2: Create your 'red' template
In the folder list on the left-hand side of the editor, open the 'Templates' folder and click 'New Template'
Select 'Collection' as the type and give it a name that makes sense.
Step 3: Make any desired updates to the file to show off your redness.
Eg: Where images are being displayed, first loop through the variants on the product and get the image of the first variant with 'red' as an option value.
Depending on how your theme is set up, you may need to edit and/or duplicate-and-change one or more snippets. You can find which one(s) by following the 'include' and 'section' tags. The 'include' tag points to files in the 'snippets' folder, and the 'section' tag points to files in the 'sections' folder.
For drastic changes, I would recommend creating new snippets and including those in your alternate template instead. For minor changes, though, you can find out if you're on an alternate template through the liquid variable template.suffix
For example, you could loop through variants to find one where variant.title contains template.suffix - then you could make collections.blue, collections.green, etc. and not need to make further edits to the snippet.
Step 4: Preview your alternate template to make sure it does what you want
If your collection is called 'shirts' and your alternate template is simply called 'red', you would preview your template as: /collections/shirts?view=red - the view=red part is what tells Shopify to load the alternate template.
Step 5: Repeat steps 3 & 4 until you're happy with the results.
Step 6: Apply the new collection template to the collection(s) you want to default to this cool (hot?) new style.
In the Shopify admin, select (from the left-hand navigation) 'Products' then 'Collections'
Select the collection you want to apply the template to
Now that you have at least 1 alternate template, a template selector should now be visible in the right-hand column.
Change the template from the default to your new one, click save, kick back and relax!
Finding an appropriate image to show
This part will vary in complexity depending on how your products are set up. I am going to assume that there is an option on each product named 'Color', but that 'Color' can be any of the three option columns on the product (ie, that we can't assume that the first option will always be the colour option)
Step 1: Make a new snippet to contain the image-finding logic (it keeps our code clean and reusable)
In the theme editor, make sure the 'Snippets' folder is expanded in the right-hand pane
Click the 'Add new snippet' link
Give the file a meaningful name (such as 'find-color-image')
Step 2: In your alternate collection template, find your product loop and include the new snippet
First, find the product loop. It should look something like {% for product in collection.products %}
Immediately after the above line, add {% include 'find-color-image', color: template.suffix, product:product %}(assuming that your template name matches the colour you are looking for - otherwise, change template.suffix to the colour you want (wrapped in quotes), such as 'red')
Step 3: Build the find-color-image snippet. The following code should do what you're looking for.
{% comment %} find-color-image.liquid: Given a product and a colour,
set a variable named color_image as the first variant image that matches the specified colour {% endcomment %}
{% comment %} Clear any leftover variables from previous includes {% endcomment %}
{% assign color_image = nil %}
{% comment %} Loop through the variants to find one matching our colour {% endcomment %}
{% for variant in product.variants %}
{% comment %} We don't care about any variants without a featured image - skip those using continue {% endcomment %}
{% unless variant.featured_image %}{% continue %}{% endunless %}
{% comment %}Make sure the comparison is case-insensitive. The variable named color is expected to be passed when this snippet is included {% endcomment %}
{% assign opt1 = variant.option1 | downcase %}
{% assign opt2 = variant.option2 | downcase %}
{% assign opt3 = variant.option3 | downcase %}
{% assign target_color = color | downcase %}
{% comment %} Check to see if one of the above options matches our target colour {% endcomment %}
{% if opt1 == target_color or opt2 == target_color or opt3 == target_color %}
{% assign color_image = variant.featured_image %}
{% break %}
{% endfor %}
{% endfor %}
Step 4: Update image links in your collection template
Change references to the product's image to the color_image variable set in the above snippet.
Hope this helps!

Shopify liquid: How can I conditionally include snippets in Shopify liquid?

I would like to include a snippet in a template but only if the snippet file exist. Is there any way I can do it?
Now I'm just using:
{% include 'snippetName' %}
But this throws the error:
Liquid error: Could not find asset snippets/snippetName.liquid
The reason I need such a functionality is because I have a background process that adds the snippet later on.
Had this problem myself. This was my solution:
{% capture the_snippet_content %}{% include the_snippet %}{% endcapture %}
{% unless the_snippet_content contains "Liquid error" %}
{% include reviews_snippet %}
{% endunless %}
Basically capture the snippet’s content as a variable.
If there is no snippet Shopify generates the error:
Liquid error: Could not find asset
snippets/caroline-flint-reviews.liquid
So check to see if it’s generated that… if so don’t print the snippet
:D
Of course this would break if you intended your snippet to include "Liquid error" or if Shopify ever change the error message.
Extending on Jon's answer;
Create a file called snippet.liquid
{% capture snippet_content %}{% include snippet %}{% endcapture %}
{% unless snippet_content contains "Liquid error" %}
{{ snippet_content }}
{% endunless %}
Then when you want to include a file only if it exists
{% include 'snippet' with 'filename_of_include' %}
Okay, Coming here in 2021.
The include syntax is deprecated and infrequently used, also extending #a.wmly answer, this should be the latest syntax replacing include with render:
{% capture snippet_content %}{% render 'your-snippet-name' %}{% endcapture %}
{% if snippet_content contains "Could not find asset" %}
{% comment %} do nothing {% endcomment %}
{% else %}
{% render 'your-snippet-name' %}
{% endif %}
references for include vs render : https://shopify.dev/docs/themes/liquid/reference/tags/deprecated-tags#include
Alternatively, you could create your own tag which does a check on the existence of the file, before attempting to process it.
https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-tags
#vovafeldman Not sure why you can't have a blank snippet, but there's no file exists.
The only other option I can think of is since you are using a BG process to generate the snippet (and I assume upload it), you can always use the template API to upload the version of the template that includes the snippet at the same time.
Using the code listed above by Jon or a.wmly both still gave me errors. However, simply writing
{% include 'snippet_name' %}
worked just fine.
Note that this only worked for files located in the "snippets/" folder. So Templates, for instance, did not work using this method.