Looping through Menu Items and output based on parent

Hey guys, trying to loop through a Menu item to output only only the sub-level items of the parent menu item. Wondering if anyone has any tips or tricks for this.

For example, my “Primary Menu” has:

  • About
  • Services
  • Portfolio

Inside the parent item “About” I have:

  • About (id: 687)
    • Brief History (id: 5)
    • Staff (id: 193)
    • Board (id: 482)

I am creating an aside menu on a page, and want to output the About Menu item and its child items only. Trying to avoid having to create a new menu item just for the aside menus.

I can see I can target the ID of About, and I can see that each of the child items have an ID, so I only want.

Wondering if anyone has any cool liquid logic for this? I’ll try it myself and if I come up with something I’ll post it here, but until then, if anyone has any suggestions, please let me know.

Thanks,
Aaron

Hey @A3CS. In order to ‘detect’ which sub menu items are needed for the page I’d say you’d have to ensure that the parent menu item (About) has a URL set for that page (often dropdown menus don’t set any URL for the parent item). So assuming that’s ok, you’d then send your menu to a collection, so it’s accessible elsewhere on the page template. Then set up a loop through that collection where you want the sub menu to appear in your template, but have a condition to look for a match between current page URL and menu item URL. If a match found, then you can further loop through its child items and output only those.

Without the parent menu item having a URL set I’m not sure how you’d detect the relevant items - unless you used some other identifier in an unused menu item field.

Cheers… thanks @Adam.Wilson

It’s fairly straight forward Aaron.

The default menu component code populates the menu as {{this.items}} while the menu is rendered and destroys it immediately after.

You have to output the menu data to a collection:

{% component type: "menu", alias: "navbar", collectionVariable: "menuData" %}

You could use the collection to build your Primary Menu & then reuse it to construct the submenu. That would save processing the data twice.

Insert your submenu menu code where you want, referencing the collection.

You already know the ID & you are only accessing the 1st subMenu depth so your code would look something like this:

{% component type: "menu", alias: "PrimaryMenu", collectionVariable: "menuData" %}

<ul>
{% for i in menuData.items %}
    {% if i.ID == 687 %}
        {% for j in i.items %}
        <li><a href="{{j.itemUrl}}">{{j.itemName}}</li>
        {% endfor %}
    {% endif %}
{% endfor %}
</ul>

You’ll need to style it of course.

If you were picking a submenu from a 3rd or 4th layer you’d need to iterate through the collection till you found the ID.

Hope this helps

Nice one @peter.medbury. This assumes though that we know the ID and are probably only creating one submenu. But if you wanted to dynamically render a submenu on any page that had submenu items in the main nav it’s this line that somehow needs to be dynamic:
{% if i.ID == 687 %}
I first thought that matching the page URL to the menu item URL would be the way to do this. Ie. something like:
{% if i.url == request.request_url.path %}
But this won’t work as you won’t be on that parent level page…
I think to do it dynamically you’d really need some sort of matching directory structure to know what a subpages parent was.
Any thoughts on doing that?
@A3CS are you trying to do this dynamically for multiple pages or is it just a one-off where a hard-coded ID is ok?

Adam

I doubt there is any truly dynamic way to it.

You might map the menu system to a nested custom module structure but I’d be concerned at the overhead.

I’d be looking to use the Custom Attribute for the Menu Item in some way.

{{this.itemcustomattribute}}

The code to render the menu would have to be on every page where a submenu might be rendered. It would have to compare the CustomAttribute to something the code can resolve about the page being loaded.

The simplest flag might be set the CustomAttribute in the Menu Item for every page that a submenu will be loaded on. Different attribute values could trigger different submenus That way you could display any submenu on any page or no submenu if the attribute is NULL.

It’s easy enough to iterate through a multi-level level menu system.

I am working on a web site at the moment that has multiple submenus. It all depends on how many levels you want.

This code block produces a multilevel menu. It is the menu.layout for the menu.:

<div class="navbar-collapse nav-main-collapse collapse">
<nav class="nav-main">
<ul id="topMain" class="nav nav-pills nav-main">
{% if this.items -%}
{% for i in this.items -%}
{% assign isSelected = "" %}
{% if request.request_url.path == i.itemUrl %}
{% assign isSelected = "active" %}
{% endif -%}
{% if i.items != null -%}
<li class="dropdown {{isSelected}}">
<a class="dropdown-toggle" href="{{i.itemUrl}}">{{i.itemName}}</a>
<ul class="dropdown-menu">
{% for j in i.items -%}
{% if j.items != null -%}
<li>
<a class="dropdown-toggle" href="{{j.itemUrl}}">{{j.itemName}}</a>
<ul class="dropdown-menu">
{% for k in j.items -%}
{% if k.items != null -%}
<li>
<a class="dropdown-toggle" href="{{k.itemUrl}}">{{k.itemName}}</a>
<ul class="dropdown-menu">
{% for l in k.items -%}
{% if l.items != null -%}
<li>
<a class="dropdown-toggle" href="{{l.itemUrl}}">{{l.itemName}}</a>
<ul class="dropdown-menu">
{% for m in l.items -%}
{% if m.items != null -%}
<li>
<a class="dropdown-toggle" href="{{m.itemUrl}}">{{m.itemName}}</a>
<ul class="dropdown-menu">
{% for n in m.items -%}
{% if n.items != null -%}
<li><a class="dropdown-toggle" href="{{n.itemUrl}}">{{n.itemName}}</a>
{% else -%}
<li><a href="{{n.itemUrl}}">{{n.itemName}}</a>
{% endif -%}
{% endfor -%}
</ul>
{% else -%}
<li><a href="{{m.itemUrl}}">{{m.itemName}}</a>
{% endif -%}
</li>
{% endfor -%}
</ul>
{% else -%}
<li><a href="{{l.itemUrl}}">{{l.itemName}}</a>
{% endif -%}
</li>
{% endfor -%}
</ul>
{% else -%}
<li><a href="{{k.itemUrl}}">{{k.itemName}}</a>
{% endif -%}
</li>
{% endfor -%}
</ul>
{% else -%}
<li><a href="{{j.itemUrl}}">{{j.itemName}}</a>
{% endif -%}
</li>
{% endfor -%}
</ul>
{% else -%}
<li class="{{isSelected}}"><a href="{{i.itemUrl}}">{{i.itemName}}</a>
{% endif -%}
</li>
{% endfor -%}
{% endif -%}
</ul>
</nav>
</div>

Yep. Using the Custom Attribute field with some sort of logical flag sounds like the way to go.

Thanks guys, will give it a shot at some point. Cheers. If I find a solution, I’ll add it to this forum.

My previous comments were theoretical.

I finally needed this functionality - present items from a menu in a multi-column, variable length list that auto adjusts as items are added or deleted.

Maybe I could have used the new Menu Layout functionality but this does it.

I wanted 3 columns & from inspection I knew the Menu Item I wanted was the 8th.

I could have found it by iterating & checking against the itemName. Knowing exactly which Menu Item avoided the extra processing.

Parent Menu Item Found using:

<pre>{{menuData.items}}</pre>

Confirmed the Submenu Items via:

<pre>{{menuData.items[8]}}</pre>

Implemented in the following manner using Liquid to determine the number of rows in each column:

    {% assign noOfColumns = 3.0 %}
    {% assign menuItemNo = 8 %}
    {% assign noOfRows = totalPlaces | divided_by: noOfColumns | ceil %}
    {% assign sumCol2Col3 = noOfRows | times: 2 %}
    {% assign col1 = totalPlaces | minus: sumCol2Col3 %}
    {% capture col1End %}{{noOfRows | minus: 1 }}{% endcapture %}
    {% capture col2Start %}{{col1End | plus: 1}}{% endcapture %}
    {% capture col2End %}{{col1End | plus: noOfRows }}{% endcapture %}
    {% capture col3Start %}{{col2End | plus: 1 }}{% endcapture %}
    {% capture col3End %}{{totalPlaces | minus: 1}}{% endcapture %}
    <div class="row-fluid">
        <div class="col-sm-4">
            {% for i in (0..col1End) %}
                <a href="{{menuData.items[menuItemNo].items[i].itemurl}}">{{menuData.items[menuItemNo].items[i].itemName}}</a><br/>
            {% endfor %}
        </div>
        <div class="col-sm-4">
            {% for i in (col2Start..col2End) %}
                  <a href="{{menuData.items[menuItemNo].items[i].itemurl}}">{{menuData.items[menuItemNo].items[i].itemName}}</a><br/>
            {% endfor %}
        </div>
        <div class="col-sm-4">
            {% for i in (col3Start..col3End) %}
                  <a href="{{menuData.items[menuItemNo].items[i].itemurl}}">{{menuData.items[menuItemNo].items[i].itemName}}</a><br/>
            {% endfor %}
        </div>
    </div>
    </div>
1 Like