Sortiertes Navigationsmenü mit Jekyll und Liquid


68

Ich erstelle eine statische Site (kein Blog) mit Jekyll / Liquid. Ich möchte, dass es ein automatisch generiertes Navigationsmenü gibt, das alle vorhandenen Seiten auflistet und die aktuelle Seite hervorhebt. Die Elemente sollten in einer bestimmten Reihenfolge zum Menü hinzugefügt werden. Daher definiere ich eine weightEigenschaft in der YAML der Seiten:

---
layout : default
title  : Some title
weight : 5
---

Das Navigationsmenü ist wie folgt aufgebaut:

<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>

Dadurch werden Links zu allen vorhandenen Seiten erstellt, diese sind jedoch unsortiert. Der sortFilter scheint ignoriert zu werden. Natürlich mache ich etwas falsch, aber ich kann nicht herausfinden, was.


Ich habe gerade herausgefunden: Der sort macht etwas. Wenn eine Site keine bereitstellt weight, wird sie zuletzt geschrieben. Wenn es jedoch eines bereitstellt, wird es immer noch nicht danach, sondern nach dem Dateinamen sortiert.
Flyx

4
Ich glaube, dass der Sortierfilter möglicherweise nur mit Ausgabe-Markup funktioniert (Dinge, die in {{}} eingeschlossen sind, nicht {%%}). Daher funktioniert es möglicherweise nicht als Filter für die for-Schleife. Mein Kommentar basiert auf dieser Seite: github.com/Shopify/liquid/wiki/Liquid-for-Designers und besagt, dass die Filter für das Ausgabemarkup bestimmt sind.
Owen

Antworten:


75

Seit Jekyll 2.2.0 können Sie ein Array von Objekten nach jeder Objekteigenschaft sortieren. Sie können jetzt tun:

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

Und sparen Sie viel Bauzeit im Vergleich zur @ kikito-Lösung.

Bearbeiten : Sie MÜSSEN Ihre Sortiereigenschaft als Ganzzahl weight: 10und nicht als Zeichenfolge zuweisen weight: "10".

Das Zuweisen von Sortiereigenschaften als Zeichenfolge führt zu einer Zeichenfolgensortierung wie "1, 10, 11, 2, 20, ...".


1
funktioniert bei mir nicht (Jekyll 2.4.0). Ich habe die Gewichtungseigenschaft auf den Seiten wie oben beschrieben definiert, aber die Sortierung scheint sie zu ignorieren.
Eyettea

2
@eyetea du hast recht. Wir müssen zuerst eine Aufgabe machen. Ich habe meinen Code bearbeitet und er funktioniert unter Jekyll 2.4.0. ;-)
David Jacquel

Danke für die Hilfe. Ich habe auch den Code bearbeitet und den zweiten Sortierfilter entfernt, da er anscheinend nicht mehr benötigt wird.
Eyettea

Du hast recht. Ich habe es selbst bearbeitet, weil Ihre vorgeschlagene Bearbeitung von 3 Benutzern abgelehnt wurde.
David Jacquel

seltsam, ich habe gerade das "| sort: weight" gelöscht ... Warum sollte es abgelehnt werden? Wie auch immer, Problem gelöst.
Eyettea

36

Ihre einzige Option scheint die Verwendung einer Doppelschleife zu sein.

<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>

Hässlich wie es ist, sollte es funktionieren. Wenn Sie auch Seiten ohne Gewicht haben, müssen Sie eine zusätzliche interne Schleife einfügen, die nur {% unless p.weight %}vor / nach der aktuellen internen Schleife ausgeführt wird .


1
lol. Ich denke, Sie können dies reduzieren, indem Sie alles in eine einzige Codezeile komprimieren, wenn dies ein Problem darstellt. Leider hat Flüssigkeit kein {%- %}Präfix, um leere Zeilen wie erb zu kollabieren.
Kikito

3
Nur eine Ergänzung: Durch Ersetzen von (1..10) durch (1..site.pages.size) wird diese Schleife so kurz wie möglich und funktioniert unabhängig von der Anzahl Ihrer Seiten. Vielen Dank für einen dummen, aber sehr cleveren Hack :)
Markus Amalthea Magnuson

@MarkusAmaltheaMagnuson die (1..10)auf diesem Code dargestellten möglichen Gewichte. Es könnte durch ersetzt werden (1..MAX_WEIGHT), um es etwas klarer zu machen (und MAX_WEIGHT an einer anderen Stelle definiert zu haben, wie in einer Konstantendatei).
Kikito

1
Dies funktionierte für mich, außer dass die "aktive" Klasse auf dem <li> anstelle des <a>
user1020853

Dies ist eines der besten Dinge bei der Jekyll / Static-Generierung - so "hässlich" es auch sein mag, es wird nur einmal ausgeführt, was sich nicht auf die Benutzererfahrung oder die Serverlast auswirkt. Schöne Lösung!
Paul Ferrett

29

Die folgende Lösung funktioniert auf Github (benötigt kein Plugin):

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

Das obige Snippet sortiert Seiten nach Dateinamen (das nameAttribut für das Seitenobjekt wird vom Dateinamen abgeleitet). Ich habe Dateien umbenannt, um meiner gewünschten Reihenfolge zu entsprechen : 00-index.md, 01-about.md- und presto! Seiten werden bestellt.

Ein Problem ist, dass diese Nummernpräfixe in den URLs landen, was für die meisten Seiten unangenehm aussieht und ein echtes Problem bei 00-index.html darstellt. Permalilnks zur Rettung:

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

PS Ich wollte schlau sein und benutzerdefinierte Attribute nur zum Sortieren hinzufügen. Leider sind benutzerdefinierte Attribute als Methoden für die Page-Klasse nicht verfügbar und können daher nicht zum Sortieren verwendet werden:

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

Sie, mein Herr, verdienen eine Medaille. Ich wollte gerade meine Site lokal generieren und den statischen HTML-Code an GitHub senden, um dazu Plugins verwenden zu können.
Adam B

Würde dies zum Sortieren nach dem vollständigen Dateipfad funktionieren? dh {% assign sorted_pages = site.pages | sort:"path" %}und 00-directory/00-file.mdwürde vor kommen01-anotherDir/00-anotherFile.md
brenna

Es funktioniert! Vielen Dank! Ich habe seit Tagen damit gekämpft.
Brenna

2
Hi @Wojtek sort:"weight"funktioniert nur für mich, fyi. Übrigens danke für die hervorragende Lösung.
Destan

1
Sortieren nach Gewicht funktioniert! Siehe Antwort unten Vielleicht wurde das in Jekyll geändert.
Mi

15

Ich habe ein einfaches Jekyll-Plugin geschrieben, um dieses Problem zu lösen:

  1. Kopieren Sie sorted_for.rbvon https://gist.github.com/3765912 in das _pluginsUnterverzeichnis Ihres Jekyll-Projekts:

    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. Verwenden Sie tag sorted_foranstelle von forwith sort_by:propertyparameter, um nach der angegebenen Eigenschaft zu sortieren. Sie können auch reversedgenau wie das Original hinzufügen for.
  3. Vergessen Sie nicht, ein anderes End-Tag zu verwenden endsorted_for.

In Ihrem Fall sieht die Verwendung folgendermaßen aus:

<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>

4
Nur schade, dass Sie keine benutzerdefinierten Plugins für GitHub-Seiten verwenden können…: - \
Paul Wagland

3
Schön, danke fürs Teilen. Nur eine kleine Ergänzung: Falls nicht alle Ihre Elemente die angegebene Eigenschaft haben, können Sie den sort_by!Aufruf ändern , um diese Elemente zu ignorieren: sorted_collection.sort_by! { |i| i.to_liquid[@attributes['sort_by']] || 0 }(Ersetzen Sie 0 durch unendlich, wenn Sie es umgekehrt möchten).
MisterMetaphor

10

Die einfachste Lösung wäre, dem Dateinamen Ihrer Seiten einen Index wie diesen voranzustellen:

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

Die Seiten werden nach Dateinamen sortiert. Jetzt haben Sie jedoch hässliche URLs.

In Ihren Yaml-Front-Materie-Abschnitten können Sie die generierte URL überschreiben, indem Sie die Permalink-Variable festlegen.

Zum Beispiel:

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

Schön! Immer noch ein Hack, aber viel einfacher als die anderen Antworten.
flyx

Seien Sie vorsichtig damit, wenn Sie Ihre Site auf Github-Seiten verschieben. Aus irgendeinem Grund wird die Bestellung dann durcheinander gebracht. Siehe auch: github.com/plusjade/jekyll-bootstrap/issues/…
Christiaan

10

Einfache Lösung:

Weisen Sie zuerst ein sortiertes Array zu site.pagesund führen Sie dann eine for-Schleife für das Array aus.

Ihr Code sieht folgendermaßen aus:

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

Dies funktioniert in meiner Navigationsleiste, _includedie einfach ist:

<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>

Plötzlich begann dies Fehler auf _post Seiten zu werfen:Liquid Exception: comparison of Hash with Hash failed in _posts/...
sdmeyers

Dies ist die sauberste Lösung; Es gibt nur einen kleinen Fehler - der Sortierschlüssel sollte als Zeichenfolge angegeben werden, dh sort: 'weight'. Der Beispielcode wurde aktualisiert.
lfk

Die oben erwähnte Problem wird erwähnt und gelöst hier . Es kann jedoch eine Weile dauern, bis die Versionen mit GitHub-Seiten aktualisiert werden.
lfk

5

Ich habe das mit einem Generator gelöst. Der Generator iteriert über Seiten, ruft die Navigationsdaten ab, sortiert sie und schiebt sie zurück in die Site-Konfiguration. Von dort kann Liquid die Daten abrufen und anzeigen. Es kümmert sich auch darum, Gegenstände zu verstecken und anzuzeigen.

Betrachten Sie dieses Seitenfragment:

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

Die Navigation wird mit diesem Liquid-Fragment gerendert:

{% for p in site.navigation %}
<li> 
    <a  {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">{{ p.navigation.title }}</a>
</li>
{% endfor %}

Fügen Sie den folgenden Code in eine Datei in Ihrem _plugins-Ordner ein:

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

Ich habe eine ganze Weile damit verbracht, das herauszufinden, da ich für Jekyll und Ruby noch ziemlich neu bin. Es wäre also großartig, wenn jemand dies verbessern könnte.


1

Ich kann den folgenden Code erhalten, der mit Jekyll / Liquid übereinstimmt und mit Ihrer Anforderung mit Kategorie übereinstimmt:

  • erstellt Links zu allen vorhandenen Seiten,
  • sortiert nach Gewicht (funktioniert auch beim Sortieren nach Kategorien),
  • Markieren Sie die aktuelle Seite.

Darüber zeigt es auch die Anzahl der Beiträge. Alles wird ohne Plug-In erledigt.

<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="https://stackoverflow.com/{{ site.github.project_title }}{{ post.url }}">{{ post.title }}</a>
            </li>
        {% endfor %}
        </ul>
    </li>
    {% endfor %}
</ul>

Überprüfen Sie es auf unserer Netzwerkseite . Sie können auf einen Beitrag klicken, um die Navigation hervorzuheben, sowie auf einen bestimmten Link, um zur Quellseite zu gelangen, auf der deren Gewicht zugewiesen ist.


0

Wenn Sie versuchen, nach Gewicht und Tag zu sortieren und die Anzahl auf 10 zu beschränken, finden Sie hier den folgenden Code:

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

-1

Die obige Lösung von @kikito hat auch bei mir funktioniert. Ich habe nur ein paar Zeilen hinzugefügt, um Seiten ohne Gewicht aus der Navigation zu entfernen und Leerzeichen zu entfernen:

<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>

@WingLeong Ich habe keine Tests gemacht, aber das hat für mich geklappt.
Jupiter
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.