Сортированное меню навигации с Jekyll и Liquid - PullRequest
68 голосов
/ 29 января 2012

Я создаю статический сайт (без блога) с помощью 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 ]

74 голосов
/ 26 августа 2014

Начиная с 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.

edit : вы MUST назначаетеВаше свойство сортировки в виде целого числа weight: 10, а не в виде строки weight: "10".

Присвоение свойств сортировки в виде строки приведет к сортировке строки, например "1, 10, 11, 2, 20, ... "

36 голосов
/ 03 февраля 2012

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

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

29 голосов
/ 18 мая 2013

Ниже решение работает на 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 - и presto!Страницы упорядочены.

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

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

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

{% assign sorted_pages = site.pages | sort:"weight" %} #bummer
15 голосов
/ 22 сентября 2012

Я написал простой плагин Jekyll для решения этой проблемы:

  1. Скопируйте 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)
    
  2. Используйте тег sorted_for вместо for с параметром sort_by:property для сортировки по заданному свойству.Вы также можете добавить reversed так же, как оригинал for.
  3. Не забудьте использовать другой конечный тег 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>
10 голосов
/ 10 октября 2013

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

Сначала присвойте отсортированный массив 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>
10 голосов
/ 20 сентября 2012

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

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

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

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

Например:

---
layout: default
permalink: index.html
---
5 голосов
/ 08 августа 2012

Я решил это с помощью генератора.Генератор перебирает страницы, получает навигационные данные, сортирует их и возвращает обратно в конфигурацию сайта.Оттуда 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 и Ruby, так что было бы здорово, если бы кто-нибудь смог улучшитьэтот.

1 голос
/ 05 июня 2016

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

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

0 голосов
/ 04 февраля 2015

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

Решение выше @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>
...