Conditionally embedding a non-product element in a Shopify collection grid - shopify

This seems like an easy one, but after researching and banging around for a couple of hours I still can't figure it out. My goal is to insert a non-product HTML block in the Shopify collection grid, like the "Enjoy Free Ground Shipping" block in row 1, col 3 here:
http://www.katespade.com/designer-handbags/handbags,default,sc.html
My collection grid is set to 3 columns by 4 rows per page, and I'm looking to replace the block at row 2, column 1 for all collection pages with 3 or more products.
The Liquid loop I need to modify is:
<ul class="product-grid clearfix">
{% for product in collection.products %}
<li{% cycle '', '', ' class="last-in-row"' %}>
{% include 'product-grid-item' %}
</li>
{% endfor %}
</ul>
Does anyone have any insights?

Welcome to Stack Overflow! :-)
First, let me pseudocode this to make sure we're on the same page, and that I've got the logic right.
for each product in collection
{
is this the 4th iteration of the loop?
(in other words, is it the first item in the second row)
{
Add an <li> for the custom non-product block.
}
Add an <li> for the standard product block
}
If that logic fits what you're looking for, here's the real thing in Liquid.
<ul class="product-grid clearfix">
{% for product in collection.products %}
{% if forloop.index == 4 %}
<li{% cycle '', '', ' class="last-in-row"' %}>
{% include 'your-custom-block-element' %}
</li>
{% endif %}
<li{% cycle '', '', ' class="last-in-row"' %}>
{% include 'product-grid-item' %}
</li>
{% endfor %}
</ul>
You may have noticed that Shopify deals with 1-based indices by default. That's why we're looking for forloop.index == 4. In almost every other language we'd be dealing with a zero-based index and checking if forloop.index == 3.
If this convention bugs you, you can always use forloop.index0 to check for the loop's index with a base of zero instead. ;-)
Please let me know if this does the trick for you. Good luck!

Related

Duplicate names in sitelinks in google search (Shopify website)

So if you Google "pets go here" or "petsgohere", you will see that the site links under my client's website https://www.petsgohere.com/ 2 of the site links have duplicate names:
"Dog Dog Store" (https://petsgohere.com/pages/dog-store)
"CAT Cat Store" (https://petsgohere.com/pages/cat-store)
What puzzles me is that I don't see duplicate names in the admin area either:
When I open up my theme.liquid, track down how the <title> element, here is how it's rendered:
{% capture seo_title %}
{{ page_title }}
{% if current_tags %}
{%- assign meta_tags = current_tags | join: ', ' %} – {{ 'general.meta.tags' | t: tags: meta_tags -}}
{% endif %}
{% if current_page != 1 %}
– {{ 'general.meta.page' | t: page: current_page }}
{% endif %}
{% unless page_title contains shop.name %}
– {{ shop.name }}
{% endunless %}
{% endcapture %}
<title>{{ seo_title }}</title>
Would anyone out there have any insight on how to resolve this issue or at least know what the issue is?
Can anyone break the code down piece by piece?
In your theme, you are rendering the element with Liquid. That is where you will find some code duplicating your title words. Ensure you render just the right data and not some crazy marketing scheme that is clearly not working!
First thing in your seo_title captured is the page_title. As per the documentation: https://help.shopify.com/en/themes/liquid/objects/page-title
Then the capture will deal with pages in case of more than 1, and the shop name. You can strip this down to be whatever you want. The Liquid capture element simply collects all things rendered in a giant string. So as per my answer, this pattern was invented long ago, to try and make some auto-sense of things in Shopify, and it is not really SEO special, it is simply a mechanical dump. You can do better depending on how you choose to appear. Remembering that page_title is for HUMANS to read, this pattern is weak at best.

Display a set of collections that include a product with a specific tag

I'm trying to set up a page which displays a list of collections, like the general 'list-collections.liquid', but only display collections that contain products with a certain product tag.
I've tried doing it like this:
{% assign var = 'test' %}
{% assign tagtrue = false %}
{% for collection in collections %}
{% unless collection.handle == 'frontpage' %}
{% for product in collection.products %}
{% for tag in product.tags %}
{% if tag contains var %}
{% assign tagtrue = true %}
{% endif %}
{% endfor %}
{% endfor %}
{% if tagtrue == true %}
<a href="{{ collection.url }}" title="{{ 'collections.general.link_title' | t: title: title }}">
{% if collection.image != blank %}
{{ collection | img_url: '480x480' | img_tag: collection.title }}
{% elsif collection.products.first != blank %}
{{ collection.products.first | img_url: '480x480' | img_tag: collection.title }}
{% else %}
{% capture current %}{% cycle 1, 2, 3, 4, 5, 6 %}{% endcapture %}
{{ 'collection-' | append: current | placeholder_svg_tag: 'placeholder-svg placeholder-svg--small' }}
{% endif %}
</a>
<p>
{{ collection.title }}
</p>
{% endif %}
{% endunless %}
{% endfor %}
But when I go to the collection list page this still returns all collections. Any ideas how to do this?
If you look at the documentation, you'll find that a product has a special method on it labelled collections. In other words, that gives you a list of all the collections the product belongs to.
So now on to your question. You ask how you can display a list of collections, but only the ones that contain products with a specific tag. OK... fair enough. You will blow speed out the window, but a simple algorithm might be:
create a collection of products with tag foomanchu
iterate the products in that collection, and use the product.collections
for each collection listed, add it to a list if it has not already been added
Once done that loop, you have the answer. A list of collections for products with a specific tag.
Good luck with performance on that, and remember to keep it simple. That looping is fast for a small shop with limited inventory, but if you have many thousands of skus with a tag spread out across many collections, you'll be challenged for instant rendering.
You can get a speed improvement over David Lazar's answer by using Shopify's map filter.
As with his answer, step 1 is to create a collection with the rule "Product contains tag" and enter the tag that you are using.
Now assuming that you have that collection object in a Liquid variable named collection, you would get the handles of all the collections through a simple map command, followed by a uniq command to strip out all the duplicates:
{% assign all_collection_handles = collection.products | map: 'collections' | map: 'handle | uniq %}
The map filter gets very specific information from the wad of objects, so it's quite a bit faster than iterating through a lot of big objects with a slew of fields you don't care about.
(Note that uniq only works on strings, numbers, or other simple data types, which is why we map all the way to the collections' handles)
Now you can iterate through all_collection_handles to do what you need to do:
{% for handle in all_collection_handles %}
{% assign collection = collections[handle] %}
<!-- Cool stuff here -->
{% endfor %}
Since you should have a much shorter list of collections than you do of products, this should be reasonably performant. Be aware as always that the more heavy-lifting that you're doing in the Liquid code the more likely you'll get page-load delays, so keeping your loops short and using focused filters such as map whenever possible will help keep things running as fast as possible.
Note: If your page starts to suffer from extreme page-load lag, you may want to skip doing this as part of the page-load and just leave a placeholder element, then use Javascript to fetch the information you need and create the display you want.

Get correct pagination results when hiding unavailable products

How can I hide a product when it is unavailable? I am really close to figuring this out but my current problem is the pagination. Its saying there are 28 results.. when there should be 24 (4 products are sold out).
Here's what I have in my collection template..
{% assign number = section.settings.products_per_page %}
{% paginate collection.products by number %}
{% for product in collection.products %}
{% if product.available %}
{% include 'product-listing' %}
{% endif %}
{% endfor %}
{% include 'pagination' %}
{% endpaginate %}
Sadly you can't modify the pagination in any way. If for example you have a full page with sold out products no products will be shown.
The only way to modify this is to create a separate "smart collection" that will take products with bigger inventory stock than 0 and loop that one instead of the current one.
Solution 1. Use your own pagination.
You can use your own pagination. It is really easy. If you have this structure:
<ul>
{% for product in collection.products %}
<li {% if forloop.index > 8 %}style="display: none;"{% endif %}>product</li>
{% endfor %}
</ul>
<a class="showmore">show more</a>
... you can use this jQuery:
$(".showmore").click(function() {
for (i = 0; i < 8; i++) {
if($(this).prev().find('li:hidden').length==0) $(this).remove();
$(this).prev().find('li:hidden').first().css('display','block');
}
});
Note that in a large store you might want to put the 'src' attribute of images in a 'data-src' attribute and replace them on toggle/show. This prevents all images being loaded at once in the overview.
Solution 2. Not hiding, but marking.
You could also show SOLD OUT next to your product, instead of hiding it. This is the easiest solution by far.

Adding a Variable in For Loop Liquid

Is it possible to assign a variable and use that variable in the for loop statement?
I'm trying to place a collection of products in a blog without having to create a new for loop for each article that needs a collection.
What I've done is using the article's tags and splitting just before the collection handle so I can inject it in the for loop, but it's not quite working dynamically yet...
Here's what I've done.. I added in the article tags
collection_some-page-handle
Then in the article.liquid
{% for tag in article.tags %}
{% assign tagMap = tag | split: '_' %}
{% if tagMap.first == 'collection' %}
{% assign collectionName = tagMap.last %}
{% endif %}
{% endfor %}
{{collectionName}} <!-- this shows the handle of the collection -->
{% for product in collections.collectionName.products limit: 8%}
<div class="productWrap">
<img src="{{ product.featured_image | product_img_url: 'medium' }}" alt="">
<p class="product_title" style="border-top:1px solid #efefef;">{{ product.title | split: ' - ' | first }}</p>
</div>
{%endfor%}
Now if i try to put the variable in the for loop it doesnt work, but of course if i put the actual handle it works. Is there a way to do it dynamically?
Instead of using collections.collectionName use collections[collectionName].
This should fix your issue if your variable has the proper handle indeed.
To clarify when you use collections.collectionName you say - "Get me a collection that have handle collectionName`.
When you use collections[collectionName] you say - "Get me a collection using the variable collectionName as a handle."

Retrieve all Shopify collections matching specific tag

I'm trying to retrieve all Shopify collections for our store which have products matching tag dog.
{% for collection in collections %}
{% assign gato = 'false' %}
{% assign perro = 'false' %}
{% for tag in collection.tags %}
{% if tag == 'Cat' %}
{% assign cat = 'true' %}
{% elsif tag == 'Dog' %}
{% assign dog = 'true' %}
{% endif %}
{% endfor %}
{% if dog == 'true' and cat == 'false' %}
<li>{{ collection.title | link_to: collection.url }}</li>
{% endif %}
{% endfor %}
I successfully get this list when I'm at homepage (telepienso.com). (See footer screenshot: A). I have the same exact code in collection.liquid and I get some of the collections but NOT all of them. (telepienso.com/collections/all). (See list on the right screenshot: B). Is there any restriction inside collection.liquid which can affect?
A screenshot (productos para perros list):
B screenshot (sección perros list):
This was the cause of the problem. The collections variable was being paginated.
By moving the code in the question outside of the paginate liquid tag, all collections are displayed in the sidebar (the same as the footer).
EDIT: The above link is broken because the question was deleted due to low traffic. I've copied the content from the question below for reference.
Shopify - issue accessing the collections global variable inside paginate
I would like to be able to access the collections global variable from within a paginated group of products, but the collections variable is also paginated if it is accessed within a paginate liquid tag.
For example (in collection.liquid):
{% for collection in collections %}
{{ collection.title }}
{% endfor %}
<br />
{% paginate collection.products by 4 %}
{% for collection in collections %}
{{ collection.title }}
{% endfor %}
...
{% endpaginate %}
Output:
All Collection1 Collection2 Collection3 Collection4 Collection5 Collection6 Frontpage
All Collection1 Collection2 Collection3
The for loop before the paginate tag lists all collections as you would expect, but doing the same thing within the paginate tag causes collections to be paginated as well as the products I am actually wanting to paginate.
Is there a way to access the collections global variable within a paginated group of products without it also being affected by the pagination?
Why would I want to do this? It was causing this problem, and was not immediately obvious because the code using the collections variable was in a separate snippet to the code with the pagination.
EDIT 2: I can no longer reproduce this issue, it appears to have been fixed.