Menus are essential for every web project. Here you can find examples for a header menu, which is automatically generated from the page tree and a footer menu, which is customizable but automatically added at the end of each page.
Neos comes with an integrated menu creator. In this example, we overwrite the template path and use our own HTML template. The result is my header menu. I use the Bootstrap Navbar.
prototype(Arsors.Neos:HeaderMenu) < prototype(Neos.Neos:ContentComponent) {
// THIS IS THE HEADERMENU MARKUP
site = ${site}
menuItems = Neos.Neos:MenuItems
renderer = afx`
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<Neos.Neos:NodeLink attributes.class="navbar-brand" node={site} content={site.properties.title} />
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<Arsors.Neos:HeaderMenu.Layer1 items={props.menuItems} />
</ul>
<span class="navbar-text">
<Arsors.Neos:DimensionsMenu />
</span>
</div>
</nav>
`
}
prototype(Arsors.Neos:HeaderMenu.Layer1) < prototype(Neos.Fusion:Component) {
// THIS IS THE MAIN NAVIGATION
// items comes from <Arsors.Neos:HeaderMenu.Layer1 items={items} />
renderer = afx`
<Neos.Fusion:Loop items={props.items} item="item">
<li class={["nav-item", item.state]}>
<a @if.hasDropdown={item.subItems} class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{item.node.properties.title}</a>
<Neos.Neos:NodeLink @if.hasNoDropdown={!item.subItems} node={item.node} attributes.class="nav-link" />
<div @if.hasDropdown={item.subItems} class="dropdown-menu" style="left: auto !important;" aria-labelledby="navbarDropdown">
<Arsors.Neos:HeaderMenu.Layer2 items={item.subItems} />
</div>
</li>
</Neos.Fusion:Loop>
`
}
prototype(Arsors.Neos:HeaderMenu.Layer2) < prototype(Neos.Fusion:Component) {
// THIS IS THE DROPDOWN
// items comes from <Arsors.Neos:HeaderMenu.Layer2 items={item.subItems} />
renderer = afx`
<Neos.Fusion:Loop items={props.items} item="item">
<Neos.Neos:NodeLink node={item.node} attributes.class={["dropdown-item", item.state]} />
</Neos.Fusion:Loop>
`
}
Because we want to have the HeaderMenu on each site, we link it to the Default.html. Therefore we need to add menu = Arsors.Neos:HeaderMenu in body > parts to the Root.fusion.
prototype(Arsors.Neos:DefaultPage) < prototype(Neos.Neos:Page) {
[...]
body {
[...]
parts {
menu = Arsors.Neos:HeaderMenu
}
[...]
}
}
[...]
<f:section name="body">
<nav class="menu container">
<div class="row">
<div class="col-12">
{parts.menu -> f:format.raw()}
</div>
</div>
</nav>
</f:section>
[...]
The example of the footer menu does not use the integrated Neos.Neos:Menu generator. A content collection is used to manually add node types or links to the footer menu. I also add a piece of code so that the footer must be added once and then generate automatically at the end of each page. If you change the footer menu on any page, you overwrite the content of the footer content collection on the root page. You will notice the orange line next to the root page in the page tree when you change the footer menu on a subpage.
'Neos.Neos:Document':
childNodes:
footer:
type: 'Neos.Neos:ContentCollection'
prototype(Arsors.Neos:DefaultPage) < prototype(Neos.Neos:Page) {
[...]
body {
[...]
parts {
[...]
footer = Neos.Neos:ContentCollection {
nodePath = ${q(site).find('footer').property('_path')}
collection = ${q(site).children('footer').children()}
}
}
[...]
}
}
<!DOCTYPE html>
{namespace neos=Neos\Neos\ViewHelpers}
{namespace fusion=Neos\Fusion\ViewHelpers}
<html>
<head>
[...]
</head>
<body>
<f:section name="body">
[...]
{parts.footer -> f:format.raw()}
[...]
</f:section>
[...]
</body>
</html>