I have a collection in my jekyll site that has files named as numbers. 1, 2, 3, ... 10, 11, 12, and so on. I'm building these pages to connect with each other, so 1 would connect to 2, ...
When I build, jekyll serves it in the order of 1, 10, 11, 12, 2, 3, ...
How can I have it build in proper numerical order?
After a little more searching, I found this answered question: How to change the default order pages in jekyll
Basically, what I came up with was:
{% assign ordered_pages = site.pages | sort:"title" %}
{% for page in ordered_pages %}
{{ page.title }}
{% endfor %}
Which is nearly identical to the original answer.
First, add order_number to your YML, like this:
---
title: anything
order_number: 10
---
Then, use the following code to order your pages:
{% assign ordered_pages = site.pages | sort:"order_number" %}
{% for page in ordered_pages %}
{{ page.title }}
{% endfor %}
I use '_number' to be compatible with CloudCannon (specifying the input type). If you don't care about that, you can just use 'order' as a variable name. I also number like this: 10, 20, 30, 40, etc. This enables me to insert new pages without changing everything.
Related
I have two list objects of the same length with complementary data i want to render is there a way to render both at the same time ie.
{% for i,j in table, total %}
{{ i }}
{{ j }}
{% endfor %}
or something similar?
If both lists are of the same length, you can return zipped_data = zip(table, total) as template context in your view, which produces a list of 2-valued tuples.
Example:
>>> lst1 = ['a', 'b', 'c']
>>> lst2 = [1, 2, 3]
>>> zip(lst1, lst2)
[('a', 1), ('b', 2), ('c', 3)]
In your template, you can then write:
{% for i, j in zipped_data %}
{{ i }}, {{ j }}
{% endfor %}
Also, take a look at Django's documentation about the for template tag here. It mentions all possibilities that you have for using it including nice examples.
For any recent visitors to this question, forloop.parentloop can mimic the zipping of two lists together:
{% for a in list_a %}{% for b in list_b %}
{% if forloop.counter == forloop.parentloop.counter %}
{{a}} {{b}}
{% endif %}
{% endfor %}{% endfor %}
Use python's zip function and zip the 2 lists together.
In your view:
zip(table, list)
In your template, you can iterate this like a simple list, and use the .0 and .1 properties to access the data from table and list, respectively.
If it's just the variables i and j that you're looking at then this should work -
return render_to_response('results.html',
{'data': zip(table, list)})
{% for i, j in data %}
<tr>
<td> {{ i }}: </td> <td> {{ j }} </td>
</tr>
{% endfor %}
(credit to everyone else who answered this question)
Rather than using a dictionary (which does not guarantee any kind of sorting), use the python zip function on the two lists and pass it to the template.
You'll have to do this in the view - use the builtin zip function to make a list of tuples, then iterate over it in the template.
Template logic is purposely simple, anything even slightly complex should be done in the view.
Short Version:
Why does the following code not produce an output when navbox.next_article is the string '2018-01-05-man-command'?!
{% capture np %} {{ site.posts | where:"post","navbox.next_article contains post.title" }} {% endcapture %}
The next post is {{ np.title }}
Details
My post 2018-01-05-man-command.md has a YAML front matter:
---
layout : post
title : 'Man Command'
tags : [RHCSA, RHCSA_mod, Using Essential Tools, Man Command]
categories: [RHCSA]
navbox:
# prev_article:
next_article: 2018-01-05-understanding-globbing-and-wildcards
---
This is accessed by the _includes/post.html file through:
{% unless include.excerpt %}
{{ post.content }}
{% include navbox.html navbox=page.navbox %}
{% endunless %}
This is used by the _layout/post.html which sets the layout for the post:
{% include post.html post=page link_title=false %}
My navbox.html contains:
{% assign navbox = include.navbox %}
{% capture np %} {{ site.posts | where:"post","navbox.next_article contains post.title" }} {% endcapture %}
The next post is {{ np.title }}
However, all I get when I run bundle exec jekyll serve is:
The next post is
Why does that line not work? I'm new to jekyll so it's possible I've made a blunder somewhere that's intuitive to most. Please tell me what I can fix.
I believe that the capture tag only captures strings, not posts. See here for more info.
I'm not convinced that a where filter supports the contains syntax you're using. See here for more info.
On top of that, where returns an array. You have to get the first item from that array.
You need to fix these issues. Use an assign instead of a capture to store a post. And change your where filter to not use the contains syntax, which isn't valid. (Unless it's been added since the issue I just linked.)
Here is how I've done it:
{% assign post = site.posts | where:"url", targetUrl | first %}
I'm trying to write a static site with Jekyll that has a few layers to it. What's the best way to generate links to all subpages within a section?
For example, if I have a site structure like this:
landing
- Topic A
- Content 1
- Content 2
- Content 3
- Topic B
- Content 1
- Content 2
- Content 3
What would be the best way to create links to each of the Content pages from its Topic page? And, is there a simple way to link to all the Topic pages from the landing?
These are not posts, just static pages. It would be really great if I could just do {% for topic.each %} ...etc. and print the links out.
I would not use posts for this purpose (as yaitloutou suggests). I would read the hierarchy from the directory structure (solution 1) or create two seperate collections (solution 2). You can let the collections from solution 2 share the same layout if you want that.
1. Using pages
Create a directory structure with index.md pages and loop over the Jekyll veriable called 'site.pages' to create the menu.
index.md
topic-a/index.md
content-1/index.md
content-2/index.md
content-3/index.md
topic-b/index.md
content-1/index.md
content-2/index.md
content-3/index.md
And loop over all pages like this:
<ul>
{% assign sitepages = site.pages | sort: 'order' %}
{% for sitepage in sitepages %}
<li {% if page.url == sitepage.url %} class="active"{% endif %}>
{{ sitepage.title }}
</li>
{% endfor %}
</ul>
If you want the nested structure, you can do something like this. Or if you want only the results for Topic A, you can do this:
<ul>
{% assign sitepages = site.pages | sort: 'order' %}
{% for sitepage in sitepages %}
{% if sitepage.url contains 'topic-a' %}
<li {% if page.url == sitepage.url %} class="active"{% endif %}>
{{ sitepage.title }}
</li>
{% endif %}
{% endfor %}
</ul>
2. Using collections (simplest solution and quickest build)
Create a collection Topic A and create another collection Topic B. Your config file should look like this:
collections:
topic-a:
output: true
permalink: /topic-a/:path/
topic-b:
output: true
permalink: /topic-b/:path/
Outputting the items of one topic goes like this:
{% assign atopics = site.topic-a | sort: 'order' %}
{% for atopic in atopics %}
<li {% if page.url == atopic.url %} class="active"{% endif %}>
{{ atopic.title }}
</li>
{% endfor %}
</ul>
You should create a _topic-a and a _topic-b directory with your content-1.md, content-2.md, etc. files.
Note that both solutions have YML variables called 'order', to determine the order of appearance of the items/pages. This looks like this:
---
title: mytitle
layout: mylayout
order: 50
---
mycontent
I'll propose here 2 ways, you can determine the "best" according to your specific needs/situation, and which one sound more adapted to them.
first of all, "posts" and "pages" are basically just collections of md/html files. with some variables associated to each one.
to generate files with this structure, you can:
1. Using _posts and page.categories
put all the sub-files in _posts (the 2017-01-01- is just a place holder)
_posts/
- 2017-01-01-content-a-1.md
- 2017-01-01-content-a-2.md
- 2017-01-01-content-a-3.md
- 2017-01-01-content-b-1.md
- 2017-01-01-content-b-2.md
- 2017-01-01-content-b-3.md
add appropriate categories to each file:
2.1. for posts caontent-a-* add category: topic-a (in this order) by adding this line in the yaml front matter at top of each of them:
---
layout: page # or any appropriate layout
category: topic-a
---
2.2. for posts caontent-b-* add category: topic-b
set a premalink to ignore the date, and create the desired structure, by adding the following line to _config.yml:
defaults:
-
scope:
path: "_posts" # to all the file in posts
values:
permalink: /landing/:categories/:title.html # set this as default permalink value
you still can specify a permalinks per post in its front matter, or just add the permalink line to each md folder front matter.
the above will generate the desired structure.
loop through all the
{% for entry in site.posts %}
{% if entry.category == type-a %}
<!-- do A stuff -->
{% elsif entry.category == type-b %}
<!-- do B stuff -->
{% endif %}
{% endfor %}
2. Using collections:
it's similar to the above, but instead of using the already existent _postscollection you'll start by creating a new collection (one advantage is that you'll not need to add a date )
any of the approaches above will generate this structure inside _site
landing/
type-a/
content-a-1/
index.html
content-a-2/
index.html
...
type-b/
...
In my markdown header I have added a custom line:
---
layout: docs
title: "My title"
date: 2015-09-18 22:40:58
permalink: /some/url/
custom: valueA valueB <---
---
And I managed to write the following template which processes these values:
{% capture custo %}{{page.custom}}{% endcapture %}
{% assign cust = custo|split: %}
{% for cus in cust%}
<code>{{ cus }}</code>
{% endfor %}
However, this seems much too complex to me. I have tried
moving the assignment directly into for, which compiles but just outputs everything as one value, not as separate ones
{% for cus in custo|split: %}
getting rid of capture, but I get undefined method 'split' for nil:NilClass doing
{% assign cust = page.custom|split: %}
Can my template be simplified or does it need to be that way? Or is it even the wrong approach?
Doing more research on this, I found out that the header is not only a header but actually YML. Therefore I can do
---
layout: docs
title: "My title"
date: 2015-09-18 22:40:58
permalink: /some/url/
custom:
1: valueA
2: valueB
---
and then use an ordinary loop
{% for cus in page.custom %}
<code>{{ cus[1] }}</code>
{% endfor %}
I'm building a simple blog using Jekyll, and I'm pulling my hair out trying to figure out this problem.
The index page of the site is meant to feature a single, most recent article, with the structure looking something like below (pardon the mess):
{% for post in site.posts reversed limit:1 %}
<div class="post">
<div class="post-inner">
<h3 class="posttitle">{{ post.title }}</h3>
<p class="postdate">{{ post.date | date: "%d %B %Y" }}</p>
{{ post.content }}
</div>
</div>
{% endfor %}
The above template works perfectly fine when the limit is not restrictive (i.e does not exist or is set to the length of the array). It seems to only be when the limit is actually restricting the result that the loop ignores reversed.
I've tried clearing the browser cache, which is what got it working without limit:1, but the progress ends there.
Thanks for the help, and I would be happy to provide more detail if this is not enough.
What is Jekyll actually outputting for you?
From what I understand, the reversed filter is applied last. So let assume you posted the first 15 days of Aug, and lets also say you do something like this:
{% for post in site.posts limit:5 %}
{{ post.content }}
{% endfor %}
You post array would be ordered in the following pattern
[Aug 15, Aug 14, Aug 13, Aug 12, Aug 11]
And then if you reversed it
{% for post in site.posts reversed limit:5 %}
{{ post.content }}
{% endfor %}
You post array would be ordered in the following pattern
[Aug 11, Aug 12, Aug 13, Aug 14, Aug 15]
With all that said, I'm a little perplexed as to why you are not using
{% for post in site.posts limit:1 %}
Hack to have custom reversed sorting by custom field with limit:
{% assign items = site.items | sort: 'some_field' | reverse %}
{% for item in items limit:10 %}
<li>{{ item.title }}</li>
{% endfor %}
It's important to assign to some custom var, sort (and reverse) there and only then loop with limit.