Short version: How do I reference a collection by variable in Liquid?
I'm building a site on Jekyll which is documentation with multiple different parts. It consists of collections of articles and there are no blogposts. Right now I have a three level menu structure defined with data files, where the third level is the actual articles. The data file look like this.
menu.yml
- title: Book
url: book
subpages:
- title: Volume 1
url: book/vol1
- title: Volume 2
url: book/vol2
- title: The Library
url: library
subpages:
- title: Getting started
url: library/getting-started
- title: Components
url: library/components
- title: Theme
url: theme
subpages:
- title: Tutorials
url: theme/tutorials
- title: Reference
url: theme/reference
Additionally, I have collections defined that match the top level items by url.
_config.yml
...
collections:
book:
output: true
library:
output: true
theme:
output: true
Right now I use the menu YML to construct myself a nice two level structure and put the articles into them to form the third level. The problem is that I don't know how to call the collections dynamically when I am constructing the menu. For example when I am creating the 'Book' menu item, I'd like to loop over site.book data collection with something like this:
{% assign collection = 'book' %}
{% for p in site.{{collection}} %}
<p>{{p.title}}</p>
{% endfor %}
site.{{collection}} doesn't work there but for example calling directly site.book does. I don't know how to pass the variable in Liquid.
What I do to get around this problem is that for every menu item that I create i loop through the whole {{site.pages}} which contains all the articles in the whole site, and match their url against the menu item url. These feels like bad programming on so many levels and I'm taking a huge performance hit when looping through everything multiple times. Generation of files goes up four times, from 5 seconds to 20 seconds right now, and it will get even worse when I make the menu even larger.
So I'm open to hearing how I could loop dynamically through the collections, or hearing about other options to create such a menu. As I have the relative url at hand, for example /book/vol1, the optimal would to find a way to get a reference to all articles within that folder (excluding those in subfolders but that is not a must-have).
The syntax for using a liquid variable inside a liquid tag looks a bit different than {% for p in site.{{collection}} %}. You can try either:
{% assign collection = 'book' %}
{% for p in site.[collection] %}
or you simply use:
{% assign books = site.book %}
{% for p in books %}
Related
How to make Shopify sort blog Posts from oldest to newest?
Now the newest post is on top. It does not work for the blog that tells the story. Naturally, you have to read a story from the begging.
Some other blogs have to be sorted in default order - from newest to older. I.e. newest top.
Q: How to make Old first sorting for some blogs?
It depends on the amount of blog posts.
There are 3 approaches.
Liquid way
You can overwrite the article paginate and reverse the loop.
Example:
{% paginate blog.articles by 9999 %}
{% for article in blog.articles reversed %}
...
{% endfor %}
{% endpaginate %}
The larger the article amount the slower the DOM load speed will be.
So if you have 1000+ articles this is not a good solution.
REST API
You can register a private app an set only the Store content like articles, blogs, comments, pages, and redirects permission to Read and everything will be disabled.
Then you will need to request the following URL: https://API_KEY:API_PASS#STORE.myshopify.com/admin/api/2020-01/blogs/BLOG_ID/articles.json?limit=250 ( multiply times if you have more than 250 articles )
And reverse the result array.
If you have many articles this solution is once again not great.
GraphQL
The best one is to use the StoreFront GraphQL API here where you can reverse the results.
Example query:
{
blogByHandle(handle:"news"){
articles(first: 50, reverse: true){
edges {
node {
title
}
}
}
}
}
This way you will keep the pagination intact ( since GraphQL return Cursor that you can use for the pagination ) and you will keep the requests at minimum.
I've actually made an app to drag and drop blogs into the positions you would like. Here's the link to the Shopify App: Article Organizer Pro
I use FOSCommentBundle in my project, I already configured it with a thread and a comment class and this is working.
But now I need to manage many types of thread and comment.
For example I have News with comments and I have Contents with comments.
Of course, the news comments are different from content comments.
I don't know how to do it in the config.yml :
fos_comment:
db_driver: orm
class:
model:
comment: Project\MyBundle\Entity\News
thread: Project\MyBundle\Entity\Comment
How can I add my other thread / comment type ? Or how can I manage this case ?
TY
In the template that you want to use FOSCommentBundle you have to put thi code:
{% include 'FOSCommentBundle:Thread:async.html.twig' with {'id': 'foo'} %}
So in the part {'id':'foo'} you can put a different id for any thread you want, even a different thread for any news, for example
{% include 'FOSCommentBundle:Thread:async.html.twig' with {'id': 'news'~news.id} %}
UPDATE FOR CLARIFICATION.
I deleted the original question because it was confusing. Maybe this one is better?
I currently have something like this (simplified for brevity):
module Jekyll
module TOCGenerator
def toc(html)
...via nokogiri, get all <h3> tags, make table of contents entry
for each.
end
def contentWithTocAnchorLinks(html)
...make "back to top" anchor links under each <h3> tag that will
take the user back up to the table of contents
end
end
end
Then in the template:
<section>{{ content | toc }}</section>
<section>{{ content | contentWithTocAnchorLinks }} </section>
This works fine, but it seems sloppy. I've also tried stuffing both toc and contentWithAnchorLinks into an array and then doing something like {{ content | tocArray | first }} which also worked, but not very well (in some cases there is no table of contents and it was confusing). Anyway, what I would like to be able to do is something like this:
{% capture toc_content %}{{ content | toc_generate }}{% endcapture %}
<section> {{ toc_content.toc }} </section>
<section> {{ toc_content.content }}</section>
Jekyll does this all the time, as in {{ page.title }} but I'm not clear on how to replicate it. I only assume that a Ruby class is involved somewhere.
Does that make more sense?
If I understand your question, you want to parse a content, get some customers infos from it, and render a table of content.
From the Information Architecture point of view, you are supposed to store your customer datas in an atomic manner. For this, Jekyll offers you data files and collections that can be very helpful in your case.
If you need to make some basic transformations on your "objects" you can use liquid or jekyll filters.
Is there a way to output (for debugging/informational purposes) the available objects and object properties in a liquid template?
That is, say I'm using the jekyll site generation tool, and I'm in my index.html template (which is, to my understanding, a liquid template). It might look something like this
{% for post in site.posts %}
<li><span>{{ post.date | date_to_string }}</span> ยป {{ post.title }}</li>
{% endfor %}
Are there any template tags I could use that would tell me/output a variable named post was available in this template (as well as the other templates). Also, are there any template tags I could use that would tell me the post object has the keys date, title, url, excerpt, permalink, etc.
There's no way to do this from a Liquid template that I'm aware of. I used the following bit of Ruby code to do it in a test for Jekyll though (setup_post is a helper method in Jekyll's test suite)
post = setup_post("2008-11-21-complex.textile")
classes = []
Liquid::Template.parse(post.content).root.nodelist.each do |token|
classes << token.name if token.is_a?(Liquid::Variable)
end
It should be possible to write a Jekyll plugin that could output this stuff on your page based on the above code.
I'm developing a plugin to facilitate multilingual Jekyll sites, and as part of this I have to categorise posts according to their language.
I'm trying to tag the post according to its language, so I have overwritten the aggregate_post_info method, but when I print the site.tags variable, it is empty.
module Jekyll
class Site
alias_method :_aggregate_post_info, :aggregate_post_info
def aggregate_post_info(post)
_aggregate_post_info(post)
#tags[post.data['lang']] << post
end
end
end
I have achieved something similar by defining my own language specific variables, like in this simplified example:
for post in site.posts.docs do
lang = post.data['lang']
for tag in post.data['tags'] do
slug = jekyll_tagging_slug(tag)
site.config['t'][lang]['tagnames'] = slug
end
end
I also automatically generate tag pages (and category pages using a different approach without plug-ins), avoid name collisions for multiple languages, and precompute aggregate counts for better performance. The whole thing is described in two blog posts http://www.guido-flohr.net/multilang-with-jekyll/ and http://www.guido-flohr.net/jekyll-multilang-tags/.
You can simply add the post language as a tag, i.e. tags: [english, ruby, etc], avoiding altogether the monkey patching. So, when you want to show only lang-tagged posts, you simply filter them:
<ul class="posts">
{% for post in site.tags.english %}
<li>
{{ post.title }}
</li>
{% endfor %}
</ul>
This way, most of the work is done by Jekyll, saving you some time and effort. :)