Whitespace control in Shopify Liquid - shopify

Shopify recently added whitespace control to the Liquid templating language:
https://help.shopify.com/themes/liquid/basics/whitespace
You essentially add an hyphen in your tag syntax {{- -}}, {%- -%} to strip whitespace (html empty line) outputted by a tag. For example:
{%- assign variable = "hello" -%}
{{ variable }}
Renders:
hello
Instead of:
 
hello
Is there a way to turns this on for all assign tags? and/or all specific control flow or iteration tags?

Indeed. You turn it on when you type your Liquid. Let your fingers do that talking!
If you load up your theme in a text editor, you can use the common Find All command to find all instances of assign. Use that to replace the surrounding {% %} with {%- -%}. Pretty much all there is to it. Repeat for any keywords you like.

Related

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.

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: Why does Liquid sometimes use {%- instead of {%?

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/

Variable within liquid if statement when calling shopify settings

I thought this would be simple to solve but I am trying to put a variable within a liquid statement.
I have my variable {{ loop_index }} and I want it to be within this statement :
{% if settings.dropdown-[loop_index]-select %}
I tried putting [...] round it but that didn't work. Basically it should say settings.dropdown-1-select, settings.dropdown-2-select.
What am I doing wrong?
Create a string containing the variable name, then use the square bracket notation to access the setting with that name. For example:
{% capture var %}dropdown-{{ loop_index }}-select{% endcapture %}
{% if settings[var] %}

Django Templates generate messy whitespace HTML

How can I make the generated HTML be cleaner in terms of whitespce?
Django Templating seems to be very sloppy about it.
For example, tags it recognizes, like IFs or FORs are parsed then replaced by an empty line.
Another example is when I include a file with N linkes of HTML code. If the include statement is tabbed, the first linke from the included file is indented propertly, the rest are pulled to the left.
And so on.
{% spaceless %} doesn't seem to do anything.
Is there a setting somewhere about how whitespace should be treated?
Or another solution?
Thank you.
I found this while looking for the answer to the same question and it seems like there isn't a clean and clear way to do this using the Django syntax (that I have found but I may have overlooked something) so on that note I'd recommend Jinja2. I have experience with using it for whitespace removal with SaltStack. One method is change {% this %} to {% this -%} which causes no newline to be appended so if you have a line containing only {% this -%} then it won't appear as anything in the generated html.
You can override the NodeList's render method as I've done. See my question with working code (applies only to the block and include tags):
Proper indentation in Django templates (without monkey-patching)?
There is a ticket in Django's issue tracker about having better handling of whitespace. It was closed as wontfix in 2014, with the following comment:
As far as I know, the consensus among the core team is that:
the Django template language is good enough for generating HTML, which isn't sensitive to whitespace;
the long term plan is to replace it with a better engine (most likely Jinja), not to keep working on it;
if you have more specialized needs (want to generate RTF?) just use another template engine, there are several to choose from.
For HTML output, I'm personally fine with the messy whitespace. It will have minimal effect on HTTP response sizes, if the responses are compressed.
I've had to work on a few cases where precise whitespace and newline control is important (for example, I have a template for Telegram messages, each newline will be a line break in the final message). To tidy up the template, I ended up writing a couple custom tags: {% linemode %} and {% line %}.
Example template:
{% linemode %}
{% line %}Line one.{% endline %}
This content will be ignored.
{% if True %}
{% line %}Line two.{% endline %}
{% endif %}
{% endlinemode %}
Result:
Line one.
Line two.
The idea here is that, the {% linemode %} block will throw away everything that is not also wrapped in {% line %} tags. That way, the {% if ... %} and other bits don't add unwanted spaces or newlines. Source here.