Сортированное меню навигации с 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, кажется, игнорируется. Очевидно, что я делаю что-то неправильно, но я не могу понять, что.

Ответ 1

С 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,..."

Ответ 2

Кажется, что ваш единственный вариант использует двойной цикл.

<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 %} до/после текущего внутреннего.

Ответ 3

Ниже решение работает на Github (не требует плагина):

{% assign sorted_pages = site.pages | sort:"name" %}
{% for node in sorted_pages %}
  <li><a href="{{node.url}}">{{node.title}}</a></li>
{% endfor %}

Выше фрагмента сортирует страницы по имени файла (атрибут name на объекте страницы происходит от имени файла). Я переименовал файлы в соответствии с моим желаемым порядком: 00-index.md, 01-about.md - и presto! Страницы упорядочены.

Один из них заключается в том, что эти префиксы номеров попадают в URL-адреса, что выглядит неудобно для большинства страниц и является реальной проблемой в 00-index.html. Permalilnks на помощь:

---
layout: default
title: News
permalink: "index.html"
---

P.S. Я хотел быть умным и добавлять пользовательские атрибуты только для сортировки. К сожалению, пользовательские атрибуты недоступны в качестве методов для класса страницы и поэтому не могут использоваться для сортировки:

{% assign sorted_pages = site.pages | sort:"weight" %} #bummer

Ответ 4

Я написал простой плагин 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>

Ответ 5

Простейшим решением было бы префикс имени файла ваших страниц с таким индексом:

00-home.html 01-services.html 02-page3.html

Страницы заказываются по имени файла. Однако теперь у вас будут уродливые URL-адреса.

В разделах вашего раздела yaml front вы можете переопределить созданный url, установив переменную permalink.

Например:

---
layout: default
permalink: index.html
---

Ответ 6

Простое решение:

Назначьте отсортированный массив site.pages сначала, затем запустите цикл for в массиве.

Ваш код будет выглядеть так:

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

Это работает в моей навигационной панели _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>

Ответ 7

Я решил это с помощью генератора. Генератор перебирает страницы, получает навигационные данные, сортирует их и возвращает их обратно в конфигурацию сайта. Оттуда Liquid может извлекать данные и отображать их. Он также заботится о том, чтобы скрывать и показывать предметы.

Рассмотрим фрагмент этой страницы:

---
navigation:
  title: Page name
  weight: 100
  show: true
---
content.

Навигация отображается с помощью этого жидкого фрагмента:

{% 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 и Ruby, так что было бы здорово, если бы кто-нибудь мог улучшить это.

Ответ 8

Я могу получить код ниже работает с 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>

Проверьте это на нашей странице . Вы можете щелкнуть сообщение, чтобы выделить навигацию, а также данную ссылку, чтобы вы попали на исходную страницу, где назначается их вес.

Ответ 9

Если вы пытаетесь отсортировать по весу и по тегу и ограничить число до 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 %} 

Ответ 10

Решение, высказанное @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>