Сортированное меню навигации с Jekyll и Liquid
Я создаю статический сайт (без блога) с помощью Jekyll/Liquid. Я хочу, чтобы в нем было автоматически сгенерированное навигационное меню, в котором перечислены все существующие страницы и выделена текущая страница. Элементы должны быть добавлены в меню в определенном порядке. Поэтому я определяю weight
Свойство в YAML страниц:
---
layout : default
title : Some title
weight : 5
---
Меню навигации построено следующим образом:
<ul>
{% for p in site.pages | sort:weight %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
{{ p.title }}
</a>
</li>
{% endfor %}
</ul>
Это создает ссылки на все существующие страницы, но они не отсортированы, sort
фильтр, похоже, игнорируется. Очевидно, я делаю что-то не так, но не могу понять, что.
10 ответов
Начиная с Jekyll 2.2.0, вы можете сортировать массив объектов по любому свойству объекта. Теперь вы можете сделать:
{% assign pages = site.pages | sort:"weight" %}
<ul>
{% for p in pages %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
{{ p.title }}
</a>
</li>
{% endfor %}
</ul>
И сэкономьте много времени на сборку по сравнению с решением @kikito.
редактировать: вы ДОЛЖНЫ назначить свойство сортировки как целое число weight: 10
а не как строка weight: "10"
,
Присвоение свойств сортировки как строки приведет к сортировке строк, например "1, 10, 11, 2, 20, ..."
Ваш единственный вариант, кажется, использует двойной цикл.
<ul>
{% for weight in (1..10) %}
{% for p in site.pages %}
{% if p.weight == weight %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
{{ p.title }}
</a>
</li>
{% endif %}
{% endfor %}
{% endfor %}
</ul>
Ужасно, но это должно работать. Если у вас также есть страницы без веса, вам придется включить дополнительный внутренний цикл, просто делая {% unless p.weight %}
до / после текущего внутреннего.
Ниже решение работает на Github (не требует плагин):
{% assign sorted_pages = site.pages | sort:"name" %}
{% for node in sorted_pages %}
<li><a href="{{node.url}}">{{node.title}}</a></li>
{% endfor %}
Вышеупомянутый фрагмент сортирует страницы по имени файла (name
Атрибут на объекте Page является производным от имени файла). Я переименовал файлы, чтобы соответствовать желаемому порядку: 00-index.md
, 01-about.md
- и престо! Страницы упорядочены.
Одна хитрость заключается в том, что эти префиксы номеров заканчиваются в URL, что выглядит неудобно для большинства страниц и является реальной проблемой в 00-index.html. Permalilnks на помощь:
---
layout: default
title: News
permalink: "index.html"
---
PS Я хотел быть умным и добавлять пользовательские атрибуты только для сортировки. К сожалению, пользовательские атрибуты не доступны как методы класса Page и поэтому не могут использоваться для сортировки:
{% assign sorted_pages = site.pages | sort:"weight" %} #bummer
Я написал простой плагин Jekyll для решения этой проблемы:
копия
sorted_for.rb
из https://gist.github.com/3765912 в_plugins
подкаталог вашего проекта Jekyll:module Jekyll class SortedForTag < Liquid::For def render(context) sorted_collection = context[@collection_name].dup sorted_collection.sort_by! { |i| i.to_liquid[@attributes['sort_by']] } sorted_collection_name = "#{@collection_name}_sorted".sub('.', '_') context[sorted_collection_name] = sorted_collection @collection_name = sorted_collection_name super end def end_tag 'endsorted_for' end end end Liquid::Template.register_tag('sorted_for', Jekyll::SortedForTag)
- Использовать тег
sorted_for
вместоfor
сsort_by:property
параметр для сортировки по заданному свойству. Вы также можете добавитьreversed
так же, как оригиналfor
, - Не забудьте использовать другой конечный тег
endsorted_for
,
В вашем случае использование выглядит так:
<ul>
{% sorted_for p in site.pages sort_by:weight %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
{{ p.title }}
</a>
</li>
{% endsorted_for %}
</ul>
Простейшим решением будет префикс имени файла ваших страниц с таким индексом:
00-home.html 01-services.html 02-page3.html
Страницы упорядочены по имени файла. Однако теперь у вас будут ужасные URL.
В ваших разделах, посвященных вопросам yaml, вы можете переопределить сгенерированный URL, установив переменную permalink.
Например:
---
layout: default
permalink: index.html
---
Простое решение:
Назначить отсортированный массив site.pages
сначала запустите цикл для массива.
Ваш код будет выглядеть так:
{% assign links = site.pages | sort: 'weight' %}
{% for p in links %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
{{ p.title }}
</a>
</li>
{% endfor %}
Это работает в моем Navbar _include
что просто:
<section id="navbar">
<nav>
{% assign tabs = site.pages | sort: 'weight' %}
{% for p in tabs %}
<span class="navitem"><a href="{{ p.url }}">{{ p.title }}</a></span>
{% endfor %}
</nav>
</section>
Я решил это с помощью генератора. Генератор перебирает страницы, получает навигационные данные, сортирует их и возвращает обратно в конфигурацию сайта. Оттуда Liquid может получить данные и отобразить их. Это также заботится о сокрытии и показе пунктов.
Рассмотрим этот фрагмент страницы:
---
navigation:
title: Page name
weight: 100
show: true
---
content.
Навигация отображается с помощью этого фрагмента Liquid:
{% for p in site.navigation %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">{{ p.navigation.title }}</a>
</li>
{% endfor %}
Поместите следующий код в файл в папке _plugins:
module Jekyll
class SiteNavigation < Jekyll::Generator
safe true
priority :lowest
def generate(site)
# First remove all invisible items (default: nil = show in nav)
sorted = []
site.pages.each do |page|
sorted << page if page.data["navigation"]["show"] != false
end
# Then sort em according to weight
sorted = sorted.sort{ |a,b| a.data["navigation"]["weight"] <=> b.data["navigation"]["weight"] }
# Debug info.
puts "Sorted resulting navigation: (use site.config['sorted_navigation']) "
sorted.each do |p|
puts p.inspect
end
# Access this in Liquid using: site.navigation
site.config["navigation"] = sorted
end
end
end
Я потратил довольно много времени на то, чтобы понять это, так как я довольно новичок в Джекилле и Руби, так что было бы здорово, если бы кто-нибудь смог улучшить это.
Я могу получить приведенный ниже код, в котором Jekyll/Liquid соответствует вашему требованию с категорией:
- создает ссылки на все существующие страницы,
- отсортировано по весу (работает также при сортировке по категориям),
- выделите текущую страницу.
Вдобавок к ним он показывает также номер поста. Все сделано без какого-либо плагина.
<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'>
{% assign posts = site.categories[tag] | sort:"weight" %}
{% for post in posts %}
<li class='recipe {% if post.url == page.url %}active{% endif %}'>
<a href="/{{ site.github.project_title }}{{ post.url }}">{{ post.title }}</a>
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
Проверьте это на действии на нашей сетевой странице. Вы можете щелкнуть сообщение, чтобы выделить навигацию, а также данную ссылку, чтобы перейти на исходную страницу, где указан их вес.
Если вы пытаетесь отсортировать по весу и по тегу и ограничить число до 10, вот код для этого:
{% assign counter = '0' %}
{% assign pages = site.pages | sort: "weight" %}
{% for page in pages %}
{% for tag in page.tags %}
{% if tag == "Getting Started" and counter < '9' %}
{% capture counter %}{{ counter | plus:'1' }}{% endcapture %}
<li><a href="{{ page.permalink | prepend: site.baseurl }}">{{page.title}}</a></li>
{% endif %}
{% endfor %}
{% endfor %}
Приведенное выше решение @kikito также сработало для меня. Я просто добавил несколько строк, чтобы удалить страницы без веса из навигации и избавиться от пробелов:
<nav>
<ul>
{% for weight in (1..5) %}
{% unless p.weight %}
{% for p in site.pages %}
{% if p.weight == weight %}
{% if p.url == page.url %}
<li>{{ p.title }}</li>
{% else %}
<li><a href="{{ p.url }}" title="{{ p.title }}">{{ p.title }}</a></li>
{% endif %}
{% endif %}
{% endfor %}
{% endunless %}
{% endfor %}
</ul>
</nav>