Menu from Page Matter (not config) when (index) Pages (and PDFs) are in Folders

I’m trying to avoid defining a menu in the config file and instead use the menu info in page matter. I also have some PDFs I’d like displayed in the menu. I want keep all my content organized in folders, so pages and page assets are all organized/ept in their related folders (Leaf/Page Bundle?), like this:

/content
├───/Home
│      index.md
├───/Products
│   │   index.md
│   └───/Widgets
│   │   index.md
│   └───/WhizzBangs
│       index.md
├───/Maintenance
│   │   index.md
│   └───/Cleaning
│   │  cleaning.pdf
│   └───/Replacing
│       replacing.pdf
└───/Contact
        index.md

The resulting (page links) menu I’m looking for is:

<ul>
  <li>Home</li>
  <li>Products</li>
  <ul>
    <li>Widgets</li>
    <li>WhizzBangs</li>
  </ul>
  <li>Maintenance</li>
  <ul>
    <li>cleaning.pdf</li>
    <li>replacing.pdf</li>
  </ul>
  <li>Contact</li>
</ul>

I’ve been tinkering with menu templates for the last day, but I’m at a loss. I’m thinking I have to use Page Bundles/Resources in a shortcode to render this menu, but I can’t get my brain around just how to code it.

Has anybody done something similar they can show me or point me to?

Thanks!

EDIT

forgot to add my page matter currently lists menu details like this:

linktitle: Products
menu:
  main:
    weight: 20

Hello @knotworking, I have edited your post for clarity. When posting code, encapsulate within code fences (three backticks).

Ahhh, backticks, I kept trying to use spaces and thinking I wasn’t getting the number of spaces right. Gave up after editing it half a dozen times. Thanks!

I realized I had it partially working, but it wasn’t rebuilding the entire site when I saved changes. I cleaned up the directory and restarted the demo server, now the menu of content pages is working. My sole challenge now is how I get the PDFs listed in the menu.

Please post the code that renders the menu.

My approach is probably wrong-headed, may not even work, but here’s what I have so far:

{{ define "menu-item" }}
  {{ $page := .page }}
  {{ with .entry }}
    {{ if .HasChildren }}
      <li class="{{ if $page.HasMenuCurrent "main" . }}active{{ end }}">
        <a href="{{ .URL }}">{{ .Name }}</a>
        <ul class="submenu">
          {{ range .Children }}
            {{ template "menu-item" (dict "entry" . "page" $page) }}
          {{ end }}
        </ul>
      </li>
    {{ else }}
      <li>
        <a href="{{ .URL }}"{{ if $page.IsMenuCurrent "main" . }} class="active"{{ end }}>{{ .Name }}</a>
      </li>
{{ if eq .Name "Maintenance" }}    
        {{ end }}
    {{ end }}
  {{ end }}
{{ end }}
<ul class="nav">
  {{ $page := . }}
  {{ range .Site.Menus.main }}
    {{ template "menu-item" (dict "entry" . "page" $page) }}
  {{ end }}
</ul>

I can match the section I want the PDFs to display under ( “Maintenance” ), but I can’t figure out how to get the links to each to display properly. I can get them to display above or below the menu by inserting this code:

{{ $allPDFs := .Resources.Match "*.pdf" }}
	{{ range $k:= (seq 0 (sub (len $allPDFs) 1)) }}
		<a href = "{{ index $allPDFs $k }}">{{ index $allPDFs $k }}</a><br>
	{{ end }}

but I can place it within the menu and (so far) I can’t call it from a separate partial.

How many PDF files on your site, now and in the future?

  • 1 - 10
  • 11 - 100
  • 101 - 1000
  • more?

Just a few (3), now and forever for this particular website. I would also like them to display in a particular order (if that’s possible).

Defining the menu in site configuration is:
a) Easier to create
b) Easier to maintain
c) Easier to troubleshoot

Structure

content
├── contact/
│   └── index.md
├── maintenance/
│   └── index.md
├── products/
│   ├── whizzbangs/
│   │   └── index.md
│   ├── widgets/
│   │   └── index.md
│   └── _index.md     <-- Make sure this is _index.md and not index.md
└── _index.md         <-- This is your home page

static
├── cleaning.pdf
└── replacing.pdf

Site configuration

[[menu.main]]
  name = "Home"
  url = "/"
  weight = 10

[[menu.main]]
  name = "Products"
  url = "/products/"
  weight = 20

[[menu.main]]
  name = "Widgets"
  url = "/widgets/"
  parent = "Products"
  weight = 1

[[menu.main]]
  name = "WhizzBangs"
  url = "/whizzbangs/"
  parent = "Products"
  weight = 2

[[menu.main]]
  name = "Maintenance"
  url = "/maintenance/"
  weight = 30

[[menu.main]]
  name = "Cleaning"
  url = "/cleaning.pdf"
  parent = "Maintenance"
  weight = 1

[[menu.main]]
  name = "Replacing"
  url = "/replacing.pdf"
  parent = "Maintenance"
  weight = 2

[[menu.main]]
  name = "Contact"
  url = "/contact/"
  weight = 40

Then use the first code snippet from this page and you’re all set.

image

Thanks Joe, wasn’t the answer that I was hoping for, but it’s what I needed to hear. For this particular site, maintaining the menu in the config isn’t a big deal.

There are buttons on top of the content edit field :slight_smile: </> is your friend.

Yep, I was using one, just turns out I should have been using the preformatted text button instead of blockquote. A code button would be nice :upside_down_face:

Oh no, I just saw what I always thought was a code button is just an inline-code button. Yeah… well… three backticks then it is :wink: sorry.

Is there a recommended way to set an “active” class on links when using the config? Seems there are several (old) ways, can’t figure out which is the “official” way…

https://gohugo.io/templates/menu-templates/

{{ $currentPage := . }}
...
...
<li class="{{ if $currentPage.IsMenuCurrent "main" . }}active{{ end }}">
  <a href="{{ .URL }}">{{ .Name }}</a>
</li>

Maybe I have something wrong, but that solution doesn’t work with my config/folders/files set as suggested above. It seems .IsMenuCurrent is not being picked up, the active class never gets set. This page addresses what seems to be the issue I’m facing: .IsMenuCurrent returns false for single pages

The workaround suggested in post #6 .IsMenuCurrent returns false for single pages - #6 by brossetti, specifically:

class=“{{ if (eq $currentPage.URL .URL) }}active{{ end }}”

does work. Active is set on the correct link using the above.