Jekyll post counts specific to custom front matter - tags

I spent the last several hours scouring the depths of stackoverflow along with other amazing Jekyll tutorial sites, and have yet to find a solution to this particular issue. =[
Instead of using site.tags or site.categories, I created my own custom label named "subcategories" under the category 'blog'. The goal is to try to get a post-count for each. The tutorials I've found have worked for both categories AND tags perfectly, just not custom front matter.
Some of my subcategories are written like this:
[design]
[gaming]
[design, gaming]
I'm looking for a code that will increment the post count by 1 as it realizes there's a post that contains the subcategory. Since I am not using a plugin, the complete list of subcategories are actually listed separately in a data .yml file (in addition to start of my posts).
Here is one of my many pitiful attempts at writing this:
<ul class="blog__sidebar__subcategories m-t-s">
{% for subcategory in site.data.subcategories %}
<a class="blog__sidebar__subcategories__item" href="{{ site.baseurl }}/blog/{{ subcategory.class }}">
<li>{{ subcategory.class }}
{% assign counter = '0' %}
{% assign subcat_data == site.data.subcategories %}
{% for post in site.categories.blog %}
{% if subcat_data == post.subcategories %}
{% capture counter %}{{ counter | plus: '1' }}{% endcapture %}
{% endif %}
{% endfor %}
({{ counter }})
</li>
</a>
{% endfor %}
</ul>
Problems I've found include the output having constant duplicates, or spitting out "design,gaming" as one entity. This results in things like:
design | 6
gamingdesign | 6
gaming | 6
gaming | 6
Here's how my .yml file looks (simple):
- class: design
- class: gaming
And my code prior to attempting to add a post count (which worked!):
<ul class="blog__sidebar__subcategories m-t-s">
{% for subcategory in site.data.subcategories %}
<a class="blog__sidebar__subcategories__item" href="{{ site.baseurl }}/blog/{{ subcategory.class }}">
<li>{{ subcategory.class }}</li>
</a>
{% endfor %}
</ul>
Also please let me know if I accidentally violated any social etiquette of stackoverflow. First time posting! Thank you a million.

Jekyll is not playing well with user arrays. I think you'd better stick to Jekyll's existing mechanisms.
So, you can use tags for subcategories.
---
layout: post
date: 2014-08-14 07:51:24 +02:00
title: Your title
categories: [ blog ]
tags:
- design
- gaming
---
And your loop can be
{% for tag in site.tags %}
{% comment %}+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
tag = Array [
"design",
Array [
#Jekyll:Post #id="/blog/1993/02/08/index",
#Jekyll:Post #id="/blog/1991/08/14/index"
]
]
Values in this tag array are :
- tag[0] -> "design"
- tag[1] -> an Array of posts that have the design tag
Here, we can already do a "{{ tag [0] }} : {{ tag[1] | size }}"
that will give us the count for each tag's posts array.
But if one post insn't in "blog" category but has a tag used
in the "blog" category, it will be counted here.
Where must count only posts that are in the "blog" category.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++{% endcomment %}
{% assign counter = 0 %}
{% for post in tag[1] %}
{% if post.categories contains "blog" %}
{% assign counter = counter | plus: 1 %}
{% endif %}
{% endfor %}
{% comment %}+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Only print counter if the current tag has "blog" posts
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++{% endcomment %}
{% if counter > 0 %}
<p>{{ tag[0] }} : {{ counter }}</p>
{% endif %}
{% endfor %}

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. :)

How to display tags in jekyll and get the tag to click through to all relevant collection posts

I am looking to display all the tags from my collection posts in a sidebar, and have each tag click through to all the relevant posts. I would also like to display the number of times the tag has been used like this:
tag_name (10)
This is what I have currently which has got all the tags as a list but I cant figure out how to get the tag to click through to all relevant pages and also display the size.
<ul class="">
{% assign tags = site.vacancies | map: 'tags' | join: ',' | split: ',' | uniq %}
{% for tag in tags %}
<li class="text-capitalize">
{{ tag }}
</li>
{% endfor %}
</ul>
First, retrieve all tags into an list by using site.tags provide by Jekyll Variables
{% capture site_tags %}{% for tag in site.tags %}{{ tag | first }}{% unless forloop.last %},{% endunless %}{% endfor %}{% endcapture %}
{% assign tags_list = site_tags | split:',' | sort_natural %}
Secondly, get a link for each Tag with its post count correspondingly
<ul>
{% for item in (0..site.tags.size) %}{% unless forloop.last %}
{% capture this_word %}{{ tags_list[item] | strip_newlines }}{% endcapture %}
<li><span class="tag-name">{{ this_word }}</span> <span class="count">{{ site.tags[this_word].size }}</span></li>
{% endunless %}{% endfor %}
</ul>
Thirdly, show each tag's name and its posts' name and date.
{% for item in (0..site.tags.size) %}{% unless forloop.last %}
{% capture this_word %}{{ tags_list[item] | strip_newlines }}{% endcapture %}
<article id="{{ this_word }}">
<h2 class="tag-heading tag-name">{{ this_word }}</h2>
<ul>
{% for post in site.tags[this_word] %}{% if post.title != null %}
<li><a href="{{ site.url }}{{ post.url }}" title="{{ post.title }}" >{{ post.date | date: '%m/%d/%Y' }} ---- {{ post.title }}</a></li>
{% endif %}{% endfor %}
</ul>
</article>
{% endunless %}{% endfor %}
So there's a way to do it by creating an array of tags while iterating through posts in the collection and using lots of liquid... and I decided to do my own workaround.
I have a master list of all the tags that I use stored in /_data/tagList.yml. Each tag has a name and slug, and you can add more fields like a description if you want to. I iterate through the data in tagList, and for each tag have a link to a dedicated page that lists all the posts that contain that tag.
If you followed the Jekyll docs and used tags in the front matter and you are consistent in naming your tags, then you can use the site.tags[tag.name] | size filter to get a count on how many posts have that tag.
Drawbacks of this workaround are:
you need to update tagList.yml any time you make a new tag
you need to make a new page for that tag (not a big deal since you can just copy/paste code from other tag pages and just change the tag you're looking for)
you need to ensure you are consistent in naming and using tags
// /_data/tagList.yml
- name: Coding
slug: coding
- name: UnpopularOpinion
slug: unpopular-opinion
// /_posts/2019-01-01-example.html
---
tags: [Coding, UnpopularOpinion]
---
// /blog/tags.html
{% for tag in site.data.tagList %}
<div>
<h2>{{tag.name}}</h2>
{% assign postCount = site.tags[tag.name] | size %}
<em>
{% if postCount == 1 %}
{{postCount}} post
{% else %}
{{postCount}} posts
{% endif %}
</em>
</div>
{% endfor %}
// /blog/tags/coding.html
{% assign numPosts = site.tags.Coding | size %}
{% if numPosts == 0 %}
<p>No posts have this tag...yet.</p>
{% endif %}
{% for post in site.tags.Coding %}
...code to display a post...
{% endfor %}

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.