11ty - How to display post tags in my post.njk file? - tags

I've used the 11ty/eleventy-base-blog template and have things working well - but I am trying to display the tags nicely within my post.njk file.
At the top of my post (.md) file I have this:
tags: ['Tag 1', 'Tag 2']
Within _includes/layouts/postslist.njk I'm using the following code:
{% for tag in post.data.tags %}
{%- if collections.tagList.indexOf(tag) != -1 -%}
{% set tagUrl %}/tags/{{ tag }}/{% endset %}
<span class="tag">{{ tag }}</span>
{%- endif -%}
{% endfor %}
This template is then used in the index.njk file (my homepage) using this code:
{% set postslist = collections.posts | head(-3) %}
{% set postslistCounter = collections.posts | length %}
{% include "postslist.njk" %}
The output of this is:
<span class="tag">Tag 1</span>
<span class="tag">Tag 2</span>
However, if I use {{ tags }} in my post.njk file I get the following output:
posts,Tag 1,Tag 2
I have also tried using the same code from the 'postslist.njk' file and putting it in my 'post.njk' file but that doesn't work.
How can I display the tags on my post in separated 'span' tags and also remove the 'posts' tag?
If you want me to open up my git repo then let me know.
Thanks!

Why is the posts tag in your tags?
In the starter template you're using (11ty/eleventy-base-blog), the posts/ directory includes a directory data file (posts.json), which gets applied too all files in that directory – i.e. to all posts. Since the .eleventy.js config file uses the Data Deep Merge option, the tags you set in each post get merged with the posts tag. This is why the tags of your post are ['posts', 'Tag 1', 'Tag 2'].
You can solve this in multiple ways. Either use two different frontmatter fields for collection and display purposes, or just filter out the posts tag when displaying the tags of a post.
How to output a list of tags?
If you just do {{ tags }}, you're telling Nunjucks to output an array, so it has to figure out how to convert an array to a string. Apparently, the default behaviour in this case is to simply join them with commas. You can make this more explicit (and include a space after the comma while you're at it):
{{ posts | join(', ') }}
Or, if you want to wrap the items in HTML tags, you can use a loop, optionally with a joiner:
{% set comma = joiner() %}
{% for tag in tags -%}
{% if tag !== 'posts' %}
{{ comma() }} <span class="tag">{{ tag }}</span>
{% 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. :)

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.

Jekyll post counts specific to custom front matter

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 %}

symfony2 form twig input value

I am doing a form with symfony2 and twig, form who get infos from BDD.
I want to customize render in function of some informations.
If my data chanson is empty, I want to show input to set it.
If my data is not empty I want to show a paragraphe who shows data, and a link for modify the value and show the input.
I try something like that :
{% if form_widget(session.chanson).attrvalue!='' %}
<p>{{form_widget(session.chanson).attrvalue}} <a>modify</a></p>
{% else %}
<p>{{ form_label(session.chanson,"Chanson : ") }}
{{ form_errors(session.chanson) }}
{{ form_widget(session.chanson) }}</p>
{% endif %}
It's not working. I try with value instead of attrvalue, it's not working neither.
Here is what symfony say : Item "attrvalue" for "<input type="text" id="form_chanson" name="form[chanson]" required="required" value="La Rage" />" does not exist in CDUserBundle:Prof:edit_session.html.twig at line 19
Did someone know the issue ?
Thank you,
You could check if the app.session.chanson variable is empty instead using:
{% if app.session.chanson %}
<p>{{ app.session.chanson }} modify</p>
{% else %}
<p>{{ form_label(app.session.chanson,"Chanson : ") }}
{{ form_errors(app.session.chanson) }}
{{ form_widget(app.session.chanson) }}</p>
{% endif %}
You then need to plug the action you want on the modify link.
Also note that if your object chanson is stored in a session, the right way to access it in your twig template is by using the app.session object.

How to customize data-prototype in symfony2 form collections?

I've got a collection of hidden fields in my form.
<ul id="user_roles">
<li><hidden field value="role1"></li>
<li><hidden field value="role2"></li>
(...)
</ul>
I use jQuery (and data-prototype) to add new roles.
The problem is that I would like to render something like this:
<ul id="user_roles">
<li>role1 <hidden field value="role1"></li>
<li>role2 <hidden field value="role2"></li>
(...)
</ul>
No problem with the initial rendering: i just put:
{% for role in roles %}
<li> {{ role }} {{ form_row(role) }} </li>
{% endfor %}
But the default data-prototype will render only {{ form_row(role) }} (a hidden field).
Where am I supposed to change the default data-prototype?
There is no {% block prototype %} in form_div_layout.html that i could customize....
The collection widget is defined as follows:
{% block collection_widget %}
{% spaceless %}
{% if prototype is defined %}
{% set attr = attr|merge({'data-prototype': form_row(prototype) }) %}
{% endif %}
{{ block('form_widget') }}
{% endspaceless %}
{% endblock collection_widget %}
So you can override this to gain control on how you want to rendre the prototype.
You can also access prototype from inside template by calling roles.vars.prototype and use it later in your JS. If you want to put it into data-prototype attribute of div (as it is normally rendered) you have to remember to escape it:
<div data-prototype="{{ form_row(roles.vars.prototype) | escape }}">
{% for role in roles %}
<li> {{ role }} {{ form_row(role) }} </li>
{% endfor %}
</div>
The method recommended in the docs allows you to easily customize each collection independently inside your app, all within the same file.
Create a file prototype_layout.html.twig:
{% block _myform_mycollection_entry_row %}
<div class="row">
<div class="col-sm-6">{{ form_row(form.title) }}</div>
<div class="col-sm-6">{{ form_row(form.author) }}</div>
</div>
{% endblock %}
The name of the block is important. The first part will be _myform if your parent form is called MyformType and the second part _mycollection if your form field owning the collection is called so. The third part must always be _entry_row in order for this to work.
For example, if you have a UserType form with a collection of 'books' the block name might be _user_books_entry_row
To make sure you got the name right, add a subform (by clicking the add button adding subforms with javascript) and inspect the id of the corresponding select html element using the inspector tool of your browser.
If it looks like user_books_0_title, then the block name will be _user_books_entry_row
Declare this file as a global form theme in the twig section of config.yml:
twig:
form_themes:
- 'AppBundle:Form:prototype_layout.html.twig' #adapt this path if you saved your file elsewhere
You can also use the file directly in your form view:
{% use "AppBundle:Form:prototype_layout.html.twig" %}