I’m trying to adopt the Menu functionality and move away from a more custom setup but I’m hoping that there’s a less manual way to use Menus. It seems like you have to declare which menu/parent a page belongs to via frontmatter, which isn’t ideal for large-scale, multi-versioned docs sites. Is there a way to set it up so that any child bundles are automatically assumed to be part of a menu as submenu entries without having to declare them in menu config/page frontmatter?
I’ve tried the “lazy blogger method”, but I think I’m doing it wrong:
I’ve added sectionPagesMenu: "main" to my hugo.yml config file.
Every nested folder in content uses a _index.md file as the parent page.
When I plug in the example code snippet to play around with it, {{ if .HasChildren }} is returning false, so nothing underneath the immediate folders are showing up.
example:
content
1.1.x
_index.md
1.5.x
_index.md
2.0.x
_index.md
latest
_index.md
Each of these have near identical IA beneath them like this:
getting started
_index.md
more children…
deployment
_index.md
more children…
I want to avoid the below because it will probably require a find-and-replace every time there’s a new docs/product version:
menu:
main:
parent: 2.4.x
Ideally, I’d also be able to make it so that if you are in /latest, it only shows the child directories/submenus of /latest. So far I’ve achieved this using sections but I’m worried I should be using Menus as a better practice:
{{$index := .Section}}
...
{{ with .Site.GetPage "section" $index }}
{{ range sort .Pages "Weight" "$order"}}
...
{{if .Pages }}
{{ range sort .Pages "Weight" "$order"}}
...
This method is looking for child menu nodes, not child pages.
For nested section menus (think of a sidebar menu on a documentation site), walk the page tree to build the items, starting with:
A collection of one or more sections, or
A menu
The trick with walking the directory tree from menu entries is to use pageRef instead of url when defining the entries. This gives you access to the page within your menu template. So, on a section page, you can access .Page.Pages, then recursively walk the page tree.
Here’s a generic example of a partial to build the tree.
layouts/partials/render-section-menu.html
{{- /*
Renders a recursive section menu starting from a page collection or menu.
As it walks the tree, this partial:
- Sets class="active" on the active list item
- Sets aria-current="page" on the active anchor
- Sets aria-current="true" on the ancestors of the active anchor
If you feed it a single page, you must wrap the page in a slice. See examples
below.
If you feed it a menu, menu entries defined in site configuration must use the
pageRef property, not the URL property. Everything must be a page.
By default, a home page reference in the page collection or menu will be
skipped. To override this behavior, set $skipHome to false below.
@param {page} currentPage The page currently being rendered.
@paran {slice} nodes A slice of top level pages or a menu.
@returns {template.HTML}
@examples
{{ with site.Menus.main }}
{{ partial "render-section-menu.html" (dict "currentPage" $ "nodes" .) }}
{{ end }}
{{ with site.Sections }}
{{ partial "render-section-menu.html" (dict "currentPage" $ "nodes" .) }}
{{ end }}
{{ with slice (site.GetPage "/introduction") }}
{{ partial "render-section-menu.html" (dict "currentPage" $ "nodes" .) }}
{{ end }}
*/}}
{{- /* Configure. */}}
{{- $skipHome := true }}
{{- /* Get parameters. */}}
{{- $currentPage := .currentPage }}
{{- $nodes := .nodes }}
{{- /* Render. */}}
<nav>
<ul>
{{- range $nodes }}
{{- if and .Page.IsHome $skipHome }}
{{- continue }}
{{- end }}
{{- template "walk" (dict "node" . "currentPage" $currentPage) }}
{{- end }}
</ul>
</nav>
{{- /* Recurively render list items. */}}
{{- define "walk" }}
{{- $currentPage := .currentPage }}
{{- $node := .node }}
{{- $linkContent := $node.Page.LinkTitle }}
{{- with $node.Name }}
{{- $linkContent = . }}
{{- end }}
{{- $ariaCurrent := "" }}
{{- $liClass := "" }}
{{- if in $currentPage.Ancestors $node.Page }}
{{- $ariaCurrent = "true" }}
{{- end }}
{{- if $currentPage.Eq $node.Page }}
{{- $ariaCurrent = "page" }}
{{- $liClass = "active" }}
{{- end }}
<li {{- with $liClass }} class="{{ . }}" {{ end -}}>
<a {{- with $ariaCurrent }} aria-current="{{ . }}" {{- end }} href="{{- $node.Page.RelPermalink }}">{{ $linkContent }}</a>
{{- with $node.Page.Pages }}
<ul>
{{- range . }}
{{- template "walk" (dict "node" . "currentPage" $currentPage) }}
{{- end }}
</ul>
{{- end }}
</li>
{{- end }}