I'm rebuilding my company's current site in Jekyll and attempting to set up the structure around a content model using objects (files in a collection) that have attributes (key/value pairs in YAML front-matter). Simple conceptual stuff designed to demonstrate effective content modeling to my team.
Anything on the site that gets reused becomes an object, with the type of object defined by the specific collection that contains its file. So, I have a "services" collection for services offered by the company, a "clients" collection, and a "people" collection with a file for each person.
The problem I'm having is referencing a specific item in a collection. For example, I want my posts to have authors. I've found a ton of solutions for this using _data, but I want my authors to have pages and collections automatically outputs a page for each person. I've been able to get _data to generate pages, but I have no control over the order in which items are listed, whereas with a collection there are a lot of ways for me to control the order.
My current solution feels hacky. I'm giving each person an id which is equal to "firstname-lastname" so in the YAML front-matter it would say id: steve-hickey. Then I use the following code to loop through every person on the site and return any that match the author-id specified in the post.
The post layout template:
---
layout: default
---
<header class="intro post-header">
<h1>{{ page.title }}</h1>
{% assign people = site.people | where:"id", page.author-id %}
{% for person in people %}
<p>Written by {{ person.first-name }} {{ person.last-name }} on <p>{{ page.date | date: '%B %d, %Y' }}</p></p>
{% endfor %}
</header>
<div class="post-body">
{{ content }}
</div>
A post's front-matter:
---
title: User Offboarding
layout: post
nav-area: blog
author-id: steve-hickey
---
A person file from the people collection:
---
id: steve-hickey
first-name: Steve
last-name: Hickey
job: User Experience Strategist & AUX Director
layout: person
nav-area: about
portrait-url:
---
It seems like there should be a way to identify a specific file or object based on a unique attribute that it already possesses, such as its name or url. That way I can just point to a specific object instead of evaluating all of them for a unique property I have to write. But after 3 days of searching I can't find a good answer to this.
If people are unique in your collection {% assign author = site.people | where:"id", page.author-id %} will return an array with one element.
In order to directly access this element do :
{% assign author = site.people | where:"id", page.author-id | first %}
This grab the first (and only) element in you array. You now do {{ author.anykey }} directly.
Related
I'm building a Shopify store and I want to create a list of recent blog posts and products, in chronological order of publication.
However I'm stumbling at the first hurdle as I am unable to access anything from the blogs. I have tried plugging both these solutions into my code and neither produces any output.
One
Two
I'm trying to show this list on the index/custom page, can the blogs global object only be accessed inside the blog template?
Any help/pointers appreciated!
Yes, you can access the global variable in Shopify on any homepage, collection, or the product page.
Here is the list of global variables into Shopify
More read about articles here.
and use like
{% assign article = articles['news'] %}
{{ article.title | link_to: article.url }}
or use blogs global object to get the articles using the handle.
{% for article in blogs.blog-handle.articles %}
{{- article.title | link_to: article.url }}
{% endfor %}
here blog-handle is your handle slug want to fetch articles.
Trying to get Jekyll collections to paginate using jekyll-paginate-v2 but I'm getting some odd behaviour where it uses the layout in the first pagination request it finds for all pages.
So for instance I have a number of different collections and for each collection I have a a folder with an index that is the same name as the collection but without the underscore.
So my events collection has _events with my markdown files and events with index.html that should display that collection. I have the same for "gallery" and "results" collections.
If I only activate pagination for one collection it works fine. As soon as I activate it for more than one the generated index.html's for every collection appear to use the first one it finds (in alphabetical order).
I've tested this by renaming folders and moving the pagination using index.html's around. Whichever index.html it hits first with pagination enabled becomes the one used for all of my index.html's that have collections enabled. Even if its in the root and called something else the same thing still happens.
My index.html's have similar front matter and pagination logic which is basically:
---
layout: page
title: Results
pagination:
enabled: true
collection: results
---
{% for result in paginator.posts %}
<a href="{{ result.url }}">
<h1 class="title results">
{{ result.title }}
</h1>
<h6 class="date results">
{{ result.date | date: "%A, %B %e, %Y at %I:%M%P" }}
</h6>
</a>
{% endfor %}
I wonder if anyone can illuminate this puzzle. I am using a new installation of vanilla jekyll on a Mac. Everything seems to work fine, but I discovered that some text being shown in my page footer was rendering differently on posts and all other pages. On most pages the text would render as HTML, but in posts it was rendering as Markdown. I found a workaround, but it left me with even more questions.
Context
I have defined footer_sections as a collection to hold portions of the footer. In my _config.yml this looks like:
collections:
footer_sections:
output: false
A footer section is then defined in a Markdown file such as _footer_sections/address.md as:
---
title: Address
order: 1
---
**My Name**
123 My Street
My Town, ST 12345
123-555-5555
In my default.html I had a footer section in my HTML something like this:
<div id="footer">
{{ site.footer_sections | where: "title", "Address" }}
</div>
And my posts are set up like this example:
---
title: Silly new post
date: 2017-02-27T12:33:53+00:00
author: Eric Celeste
layout: post
---
Silly post.
And finally, the post layout is connected to the default layout like this:
---
layout: default
---
<h1>{{ page.title }}</h1>
<p class="meta">{{ page.date | date_to_string }}</p>
<div class="post">
{{ content }}
</div>
The Problem
Notice that the address.md file is defined in Markdown and then its content is shown in the footer by the inclusion of the section in default.html. On all regular pages this would render as HTML (a bold name, a plain address), but on posts like the silly post above, it would render as Markdown (a name surrounded by stars and an address without like breaks).
I thought maybe it had to do with different procedural steps between posts and pages, maybe the Markdown rendering is happening "later" on pages but has already happened "earlier" in posts. I am only two days old on Jekyll, so I really don't know how it works.
In order to test that theory, I tried forcing the Markdown rendering with the markdownify filter. I changed the liquid tags in default.html so that they read:
{% assign section = site.footer_sections | where: "title", "Address" %}
{{ section.content | markdownify }}
Oddly, this produced a worse result everywhere. Now no text of any sort appeared in the footer of regular or post pages.
On the theory that maybe the where filter is actually different from looping through members of an array with foreach I tried another approach:
{% for section in site.footer_sections %}
{% if section.title == "Address" %}
{{ section.content | markdownify }}
{% endif %}
{% endfor %}
That worked! Now the content of the footer sections rendered as HTML on both regular pages and posts.
My Questions
Why didn't the initial approach work? What is the difference between rendering of posts and other pages in Jekyll?
While I found workaround, I don't understand why it works. In what ways does pulling out an item from an array with a where filter differ from using a member from a foreach loop? How does this affect the results of the markdownify filter?
Is there a cleaner, simpler way to grab the HTML-rendered content from my sections than looping through them each time I want to use one of them?
Thanks for any insights you may have!
site.footer_sections is an array and the output of the 'where' filter is still an array (but only containing the values that match your condition).
In your case, you are getting a single-element array but it's still an array object.
To see this for yourself use the inspect filter:
{% assign section = site.footer_sections | where: "title", "Address" %}
{{ section.content | inspect }}
On the other hand, when you loop through the elements with a for loop, at each iteration you get the individual elements of the array. Try using inspect inside your loop to see how the two types of your section variable differ.
For the 'where' method to work you need to get the actual element from the array either with first or [0]:
{% assign section = site.footer_sections | where: "title", "Address" %}
{{ section.first.content | markdownify }}
OR
{% assign section = site.footer_sections | where: "title", "Address" %}
{{ section[0].content | markdownify }}
links:
array documentation
first documentation
where documentation
I am using Jekyll as a static generator for a website (not a blog), and I want to have an automatically generated list of all pages on my index page. Specifically, I want to have different categories and list all articles in each category separately. Here's an example of what I'm describing, if you're having trouble following. Is there any way to do this in Jekyll (e.g. GitHub pages)? I've seen the variables documentation page but that seems specific to the blog post format.
While building my own site I came across this very same problem, and I have found an (IMHO) easy and robust solution.
The Problem
Given a subset of pages (not posts) on the site, list them under headings based on their categories. For example: given a set of pages which we consider resource pages (or reference pages, or whatever logical grouping of pages that you want to display are), we want to list them under their categories (ex. code, explanation, et cetera).
The Solution
To get the behaviour that we want, we have to make modifications in three places:
_config.yml
resources.md
resource-file-X.md
_config.yml
In _config.yml, we must add a list of all of the categories/keywords/tags (or whatever you want to call it) that will appear in the resource files. Here is what I have in mine:
category-list: [code, editors, math, unix]
You can call the variable anything, I chose category-list, just make sure that you use the same variable in the resource.md file.
Note: The order that you place the items in the list is the order they will be listed on the resource.md page.
resource-file-X.md
These are the files that you want to have indexed and linked to on the resources.md page. All that you need to do is add two file variables to the top of each of these files. The first is to indicate that this file is a resource file.
resource: true
The second is to indicate what categories you want this file to be indexed under. You can index it under as many categories as you would like, and if you want a page un-indexed, leave the list blank. My reference for proper EINTR handling in C has the following categories:
categories: [code, unix]
resources.md
This is the file that will generate the list of pages based on their respective categories. All you need to do is add the following code to this file (or whatever file you want the list to be on):
{% for cat in site.category-list %}
### {{ cat }}
<ul>
{% for page in site.pages %}
{% if page.resource == true %}
{% for pc in page.categories %}
{% if pc == cat %}
<li>{{ page.title }}</li>
{% endif %} <!-- cat-match-p -->
{% endfor %} <!-- page-category -->
{% endif %} <!-- resource-p -->
{% endfor %} <!-- page -->
</ul>
{% endfor %} <!-- cat -->
Code Breakdown
Just a quick explanation of how this works:
Loop through each of the categories specified in _config.yml.
Display a heading with that category name.
Start an unordered list for the pages that belong in that category.
Loop through the pages on the site.
If the page is a resource file as indicated by the file variable resource, then for each of the categories that the file belongs to, if one of them matches the current category being listed, display a link to that page.
Note: the variables category-list in _config.yml and categories in the resource files can be called whatever you want, just make sure that you use the same variables in the file generating the list.
Another Note: When you modify _config.yml, you have to completely restart Jekyll, even if you have the --watch option, you have to stop and restart it. It took me a while to figure out why my changes weren't taking effect!
The Final Product
You can see the final product on the resources page on my site, although I just put this together today so at the time of this writing, it's far from complete, but you can check out my bio if you want on the home page.
There's a cleaner way to do this using the liquid "contains" property:
In _config.yml, add your index of categories
categories: [fruit, meat, vegetable, cheese, drink]
In your page.md inside the front matter, add one or more of the categories available in the _config.yml
---
layout: page
title: Orange juice
description: Orange juice is juice from oranges. It's made by squeezing oranges.
categories: [fruit, drink]
---
In your template to get all the pages in the fruit category you do:
{% for page in site.pages %}
{% if page.categories contains 'fruit' %}
<div class="item">
<h3>{{page.title}}</h3>
<p>{{page.description}}</p>
</div>
{% endif %}
{% endfor %}
You should differentiate between pages and posts (articles). Listing all posts sorted by category is not a problem at all. You can loop through site.categories. It contains the category name and a list of all posts in that category.
Listing all pages is possible, too. You can loop through site.pages. But a page does not belong to a specific category (only posts do).
When I take a look at your posted example, using categories on posts and then looping through site.categories seems to be the way to go. It will get you exactly the desired output.
There are some variations/ simplifications possible (answer of felipesk). Maybe due to improvements in Jekyll.
There is NO index needed in _config.yml.
If the list of pages are not listed in a page but for example in a doc, you can add the category also to the doc:
---
layout: doc
title: Fruit List
categories: [fruit]
---
And then use it like this:
{% for p in site.pages %}
{% if p.categories contains page.category %}
* [{{ p.title }}]({{ p.url | absolute_url }})
<small>{{ p.excerpt }}</small>
{% endif %}
{% endfor %}
With posts this can even be shorter:
{% for post in site.categories[page.category] %}
* [{{ post.title }}]({{ post.url | absolute_url }})
<small>{{ post.excerpt }}</small>
{% endfor %}
Why this only works for posts, I could not figure out yet.
The interesting point is that this snippet can be used everywhere (if you mix docs/pages/posts)!
So just add it as an _includes and use it like:
## Further Reading
{% include pages-list.md %}
I work with the theme Minimal Mistakes
I have the following header on a jekyll post with a single author "usman" which generates the this article. I would like to have something like "authors: usman, someone_else" so a colleague can also contribute to the article. Is this possible? How would I set it up.
---
layout: post
title: The pitfalls of building services using Google App Engine -- Part I
date: 2013-02-24 08:18:17
author: usman
categories:
- System Admin
tags:
- GAE
---
I took a look at the post template for the theme I am using and it has the following line:
{% assign author = site.authors[page.author] %}
This will obviously only support one author, Is there a way to get both authors of the page? i.e. page.author[0] for example?
If you want to specify multiple authors in your YAML Frontmatter, then you are going to want to use YAML's list syntax like you did with categories and tags, like this:
author:
- usman
- someone_else
This will be useful for dynamically injecting author information into each of the posts.
As for allowing multiple people to contribute to the same article, I don't think this has anything to do with Jekyll or what is specified in the Frontmatter. This is an issue of having your Jekyll content hosted in a shared place (such as on GitHub, like many do) where both you and your collaborator can both work on the file. That being said, be aware that you may run into nasty merge conflicts if you work on that same markdown file in parallel.
Update
This is an update based on the OP's edits to the original question.
A simple hacked approach would be to set your author tag like this:
author: Usman and Someone_Else
This doesn't give you much flexibility though. A better solution, which would require you to modify the template you are using, would be to do something like the following:
First, setup your YAML Front Matter so that it can support multiple authors.
authors:
- Usman
- Someone_else
Now, you modify the template to go through the authors specified in the YAML Front Matter.
<p>
{% assign authorCount = page.authors | size %}
{% if authorCount == 0 %}
No author
{% elsif authorCount == 1 %}
{{ page.authors | first }}
{% else %}
{% for author in page.authors %}
{% if forloop.first %}
{{ author }}
{% elsif forloop.last %}
and {{ author }}
{% else %}
, {{ author }}
{% endif %}
{% endfor %}
{% endif %}
</p>
Resulting HTML:
If no authors are specified:
<p>No author</p>
If one author is specified:
<p>Usman</p>
If two authors are specified:
<p>Usman and Someone_Else</p>
If more than two authors are specified:
<p>Usman, Bob, and Someone_Else</p>