conditionally apply two different classes in liquid file --not working properly - class

I have a scenario where two IF conditions in class attribute is breaking the functionality.
only one IF conditions is working perfectly without breaking the functionality.
<li class="{% if item.dropdown %}has-dropdown{% endif %} {% if item.active %}active{% endif %}">
..........
</li>
Here class attribute has two IF conditions.
I want a way where without two IF conditions, we can achieve the same scenario in liquid template.

One way to remove the {% if %} conditions from the class attribute would be to:
Create a liquid variable that contains the class names beforehand
Print the variable value inside the class attribute
{% assign custom_classes = '' %}
{% if item.dropdown %}
{% assign custom_classes = custom_classes | append: 'has-dropdown ' %}
{% endif %}
{% if item.active %}
{% assign custom_classes = custom_classes | append: 'active ' %}
{% endif %}
<li class="{{ custom_classes }}">
...
</li>

Related

Shopify - How can I match the display order of tags to product display order in a collection?

Hoping some one can help out with this one as I'm still fairly new-ish to working with Shopify's liquid code format.
Recently I have implemented multiple dropdown sorting boxes on collection pages for product filters using tags.
Based on this "How-To": https://community.shopify.com/c/Shopify-Design/How-to-Multiple-Dropdown-Sorting-Boxes-on-Collection-...
All this is working as intended, however I am having trouble ordering the tags of one of the filters. I want the display order of the tags to match the current order of the products in the collection (which have been ordered manually from the backend) - see image
Screenshot of the collection page and filters
The code handling this specific dropdown:
<div class="four-columns">
Package size:
<select class="coll-filter" id="sizeFilter">
<option class="size-all" value="">All</option>
{% for tag in collection.all_tags %}
{% if tag contains 'size-' %}
{% assign tagName = tag | remove: 'size-' %}
{% if current_tags contains tag %}
<option class="{{tag}}" value="{{ tag | handle }}" selected>{{ tagName }}</option>
{% else %}
<option class="{{tag}}" value="{{ tag | handle }}">{{ tagName }}</option>
{% endif %}
{% endif %}
{% endfor %}
</select>
</div>
My main drawback is that I can't "hardcode" the list as there are multiple collections with different package sizes. Any and all help is greatly appreciated. Thanks.
Well if you want to be based on the order of the products, then you will need to use the products instead of grabbing all the tags.
So it will become something like this:
{% assign all_tags = '' %}
{% for product in collection.products %}
{% for tag in product.tags %}
{% if tag contains 'size-' %}
{% assign all_tags = all_tags | append: tag | append: ',' %}
{% endif %}
{% endfor %}
{% endfor %}
{% assign all_tags_unique = all_tags | split: ',' | uniq %}
<select>
{% for tag in all_tags_unique %}
{% assign tagName = tag | remove: 'size-' %}
{% if current_tags contains tag %}
<option class="{{tag}}" value="{{ tag | handle }}" selected>{{ tagName }}</option>
{% else %}
<option class="{{tag}}" value="{{ tag | handle }}">{{ tagName }}</option>
{% endif %}
{% endfor %}
</select>
Where we will loop the current products and push each of their tags to the varaible {% assign all_tags = '' %}.
After we finish with the products we will split it by our divider, which in our case is , and will add the unique filter which will remove any repeats in the array (in case you have more than one size on a product or a repeat).
And finally you will loop the newly created tag array all_tags_unique .
That's all.
PS: Have in mind that you have more than 50 products you will need to increase the pagination to get them all.
Have in mind that this is not tested code so there may be a mistake or two but the logic is there. :)

Tag count automatically changing while clicking on the product tag

I am using Shopify. I am in collection page where I am getting all the filter with tag count something like,
All Products
Apple(4)
Banana(2)
Orange(1)
Mango(8)
Now when I click on any of the tag(For example I clicked on Banana) then It will display the banana products.
Now my issue is by clicking on the tag it's changing the tag count.
All Products
Apple(0)
Banana(2)
Orange(0)
Mango(4)
I am using below code
{% for tag in collection.all_tags %}
{% assign products_count = 0 %}
{% for product in collection.products %}
{% if product.tags contains tag %}
{% assign products_count = products_count | plus: 1 %}
{% endif %}
{% endfor %}
<a class="filter__link" href="/collections/{% if collection.handle != blank %}{{ collection.handle }}{% else %}all{% endif %}/{{ tag | handleize }}"{% if current_tags contains tag %} selected="selected" id="tag_active"{% endif %}>{{ tag }} ({{products_count }})</a>
{% endfor %}
Thanks in advance.
It looks like the step you're missing is the first line here:
{% assign collection = collections.all %}
You're iterating over the current collection, so as you've noticed when you click on a tag the results change.
If you don't have a collection with the handle all, you can create one by following this process:
Go to Products > Collections.
Click Add a collection.
Create the collection:
Give your collection the Title All.
In the Conditions section, select "Automatically select products based on conditions".
Set the product condition "Product price is greater than $0".
Save
Edit:
This fixes the issue where the product count changes when you click on a tag link:
{% for tag in collection.all_tags %}
{% assign products_count = 0 %}
{% for product in collections[collection.handle].products %}
{% if product.tags contains tag %}
{% assign products_count = products_count | plus: 1 %}
{% endif %}
{% endfor %}
<a class="filter__link" href="/collections/{% if collection.handle != blank %}{{ collection.handle }}{% else %}all{% endif %}/{{ tag | handleize }}"{% if current_tags contains tag %} selected="selected" id="tag_active"{% endif %}>{{ tag }} ({{products_count }})</a>
{% endfor %}
The key part is:
{% for product in collections[collection.handle].products %}
It looks like when you're filtering by tag with a URL like collections/collection_1/tag_1 then collection.products is also filtered by the selected tag. The line above looks a bit messy, but it appears to return the full set of products.
As I was saying in comment, the issue comes from your secondary loop:
{% for product in collection.products %}
Which accesses only to the current view and not the full collection products.
I've not tested it but I guess it worthes a try:
{% assign whole_collection = collections[collection.handle] %}
{% for product in whole_collection.products %}
{% if product.tags contains tag %}
{% assign products_count = products_count | plus: 1 %}
{% endif %}
{% endfor %}
Explanation, a code like this {{ collections['the-handle'].url }} allows access to any specific collection and its attributes.
HTH
Memo : this won't work accurately if your collection has more than 50 items.

Jekyll case-insensitive sorting

I'm trying to create a tags list in Jekyll. Some of the tags are "accessibility", "CSS", and "JavaScript". So my Jekyll code to create the list looks like this:
<ul>
{% for item in (0..site.tags.size) %}{% unless forloop.last %}
{% capture this_word %}{{ tag_words[item] }}{% endcapture %}
<li>
<a href="#{{ this_word | cgi_escape }}" class="tag">{{ this_word }}
<span>({{ site.tags[this_word].size }})</span>
</a>
</li>
{% endunless %}{% endfor %}
</ul>
However, the rending of the list isn't alphabetical. It's first case-sensitive, capital words first; so my example tags above are rendered in this order:
CSS
JavaScript
accessibility
Is there a way to make the sorted list case-insensitive?
There is a sort_natural filter in liquid, but it doesn't work with site.tags.
The trick is to generate an array with all tags names
{% comment %} Creates an empty array {% endcomment %}
{% assign tags = "" | split:"" %}
{% comment %}Creates an array of tags names{% endcomment %}
{% for t in site.tags %}
{% assign tags = tags | push: t[0] %}
{% endfor %}
Sort them naturally (case insensitive)
{% assign sorted_tags = tags | sort_natural %}
Based on this sort, print tags counts
<ul>
{% for t in sorted_tags %}
<li>{{ t }} : {{ site.tags[t].size }}</li>
{% endfor %}
</ul>
This becomes more complex once you have to find a post by its name. Here's a solution to sort posts in an archive list alphabetically:
{% assign post_names = "" | split:"" %}
{% for post in tag.last %}
{% assign post_names = post_names | push: post.title %}
{% endfor %}
{% assign sorted_post_names = post_names | sort_natural %}
{% for post_name in sorted_post_names %}
{% assign matched_post = site.posts | where:"title",post_name %}
{% assign post = matched_post[0] %}
…
{% endfor %}
The trick is to find the post with where from the overall list of posts.
It seems that the mentioned issue of sort_natural filter has been fixed. You may try to use it directly:
<ul>
{% for t in site.tags sort_natural %}
<li>{{ t }} : {{ site.tags[t].size }}</li>
{% endfor %}
</ul>

Jekyll -- number of posts by custom yml tags

I am working on a jekyll / gh-pages site. I'd like to build a side bar that lists the number of posts according to a CUSTOM tag. So i can sort posts using different yml elements. It works just fine using the tags yml element and this code
<h3>Activities By Topic</h3>
{% for tag in site.tags %}
{% assign t = tag | first %}
{% for atag in site.data.tags %}
{% if atag.slug == t %}
<h5><a href="{{ site.baseurl }}/{{ atag.slug }}">{{ atag.name }}
{% endif %}
{% endfor %}
({{ tag | last | size }})
</a></h5>
{% endfor %}
But what i'd like is another block that is "Activities by Type" (we are trying to sort posts in different ways. I setup a topic-tag yml element and a `topic-tag.yml file'
_data folder
https://github.com/lwasser/data-lesson-catalog/blob/gh-pages/_data/topic-tags.yml
org folder:
https://github.com/lwasser/data-lesson-catalog/tree/gh-pages/org/topic-tag
sample post:
https://github.com/lwasser/data-lesson-catalog/edit/gh-pages/_posts/lessons/2015-09-10_dc-R.md
relevant YML from sample post
---
layout: post
catalog-entry-type: lesson
title: Data Carpentry R for Ecology
topic-tag: ["Analysis", "Vizualization"]
---
Code that is not working:
<h3>Data Activities By Topic Tag</h3>
{% for tag in site.topic-tag %}
{% assign t = tag | first %}
{% for atag in site.data.topic-tags %}
{% if atag.slug == t %}
<h5><a href="{{ site.baseurl }}/{{ atag.slug }}">{{ atag.name }}</h5>
{% endif %}
{% endfor %}
({{ tag | last | size }})
{% endfor %}
Can i sort posts by other tags (not just the tags yml element)?
The output that i'd like is something like:
Analysis (2)
Visualization (3)
If so, any suggestions as to why the code above doesn't work? I found another post on here asking something similar but the resolution was to use the yaml "tags"element which will not work for my use case.
Many thanks,
Leah
You can use categories. They are working just like tags and can be another way to sort posts.
Hi to anyone who is struggling with this same thing. This is what i've learned.
Tags and categories are built into the jekyll build. You can thus call site.tags or site.categories without adding anything to your config file. Other custom tags that you create like topic-tags, are not inherently understood by jekyl as variables that can be called at the site level. My work-around -- use a counter to count posts by custom variable. the code looks like this
{% for member in site.data.topic-tags %}
{% assign counter = 0 %}
<!-- this code counts the number of posts associated with the member -->
{% for post in site.posts %}
{% if post.topic-tag contains member.slug %}
{% assign counter = counter | plus: 1 %}
{% endif %}
{% endfor %}
<h5><a href="{{ site.baseurl }}/topic-tag/{{ member.slug }}">{{ member.name }} ({{ counter }})</h5>
{% endfor %}
This works like a charm albeit it is looking through all of the posts to see if it contains the member tag in the yaml.
I hope this helps someone!
Here the code I use to display number of post on each categories. You may change site.categories to site.tags to display them by tags.
<h2 class="question">Topics ({{ site.posts | size }} total)</h2>
<ul class="topics">
{% capture tags %}
{% for tag in site.categories %}
{{ tag[0] }}
{% endfor %}
{% endcapture %}
{% assign sortedtags = tags | split:' ' | sort %}
{% for tag in sortedtags %}
<li class="topic-header"><b>{{ tag }} ({{ site.categories[tag] | size }} topics)</b>
<ul class='subnavlist'>
{% for post in site.categories[tag] %}
<li class='recipe'>
{{ post.title }}
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
Check it on action here.

Symfony2: add class to every select

I'm trying to customize Symfony2 form rendering to add a class to every select that is generated.
I thought that having a custom form_div_layout.html.twig with:
{% block choice_widget_collapsed %}
{% spaceless %}
<select class='myclass' {{ block('widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
{% if empty_value is not none %}
<option value="">{{ empty_value|trans({}, translation_domain) }}</option>
{% endif %}
{% if preferred_choices|length > 0 %}
{% set options = preferred_choices %}
{{ block('choice_widget_options') }}
{% if choices|length > 0 and separator is not none %}
<option disabled="disabled">{{ separator }}</option>
{% endif %}
{% endif %}
{% set options = choices %}
{{ block('choice_widget_options') }}
</select>
{% endspaceless %}
{% endblock choice_widget_collapsed %}
and using it with
{% form_theme form 'YOPYourOwnPoetBundle:Form:form_div_layout.html.twig' %}
would do the trick.
However, the class 'myclass' isn't added to the select.
What am I doing wrong?
You should first make sure the theme file you're trying to use has the same name as the name you're using in the form_theme expression and that the file really is there. I can't remember off top of my head whether Twig throws an exception or not in case these do not match.
In addition, you might be accidentally passing a class attribute either when building a form or rendering it. What happens is that your element now has two class attributes.
A workaround is to actually add your new class to the collection of existing ones.
{% block choice_widget_collapsed %}
{% spaceless %}
{% set label_attr = label_attr|merge({class: label_attr.class|default('') ~ ' required'}) %}
{% set attr = attr|merge({class: (attr.class|default('') ~ ' myclass')|trim}) %}
<select {{ block('widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
{# ... #}
</select>
{% endspaceless %}
{% endblock choice_widget_collapsed %}
This allows you to add any optional class you might need for specific elements later on.
EDIT
Looking at the Sf2 Github repsitory it seems that the theme file has been recently changed. In versions 2.0.* you should be overriding choice_widget, in versions 2.1.* the correct block is choice_widget_collapsed.
I suppose you should either change the form theme line to:
{% form_theme form 'YOPYourOwnPoetBundle:Form:form_div_layout.html.twig' %}
or you need to change the name of your twig file into fields.html.twig
Both have to match.