2

So I've got a {% nav %} I'm trying to create with the following code:

{% nav page in craft.entries.section('pages') %}
    <li>
        <a href="{{ page.url }}">{{ page.title }}</a>

        {% ifchildren %}
            <ul>{% children %}</ul>
        {% endifchildren %}

    </li>
{% endnav %}

My issue is that I've got a top-level page that has been disabled but has some enabled child/sub pages (second-level). So the code above doesn't display the disabled top-level page but it is showing all the enabled children of that disabled page. I've tried wrapping different parts of the {% nav %} code with a conditional but that seems to interfere with the {% ifchildren %} tag.

So my question is, is this the default Craft functionality? If so, what's the best way to solve this problem? Thanks!

2
  • 1
    I would suggest you to build a custom menu macro for that. The nav has very limited use and it's difficult to create flexible nested menus. If you are willing to use a macro I could provide some code Commented Mar 2, 2018 at 14:30
  • @RobinSchambach for sure. Add it as an answer and I'll go ahead and accept it for now 👍️
    – Jalen Davenport
    Commented Mar 2, 2018 at 14:34

2 Answers 2

3

This is an example of our "quick and dirty" menu macro we used in older projects. It's recursive so it can have as many levels as you want and will only fetch enabled entries. For mobile devices the parent entry is rendered in the "child" menu as well because otherwise you won't be able to access parent entries with children.

It looks a bit overwhelming at first, because we had many things to consider but it's relative easy

<!--
menuCategory: [mainmenu/footermenu/topmenu/descendants]
currentPage: the current entry of the loop
includeStartPage: bool if the start page should be included to the menu
level: current level, integer
parent: parent entry if there is one
original: the original current entry from craft
showParentPage: if the parent page should appear in current menu level for mobile devices
-->
{% macro menu(menuCategory, currentPage, includeStartpage, level = 1, parent = null, original = null, showParentPage = null) %}
    {% set originalPage = (original)? original : currentPage %}
    {% set activeClass = '' %}
    {% if original is not null and currentPage is not null and original.parent is not null %}
        {% if original.parent.id == currentPage.id %}
            {% set activeClass = "active" %}
        {% endif %}
    {% endif %}

    <ul class="{{ activeClass }} {% if parent == null %}clearfix{% endif %}" data-level="{{ level }}">
        {% if includeStartpage %}
            {% set startseite = craft.entries.section('homepage').first() %}
            <li {% if currentPage.id == startseite.id %}class="active" {% endif %}>
                <a  href="{{ startseite.url }}" title="{{ startseite.title }}">{{ startseite.title|raw }}</a>
            </li>
        {% endif %}

        {% if showParentPage and parent%}
            <li>
                <a  href="{{ parent.url }}" title="{{ parent.title }}">{{ parent.title|raw }}</a>
            </li>
        {% endif %}

        <!-- you can leave that actually these are just the function to fetch the different entries
        for the different menu types
        if you have only one type just do set entries = craft.entries.section('pages').find()
        -->
        {% switch menuCategory %}
            {% case 'mainmenu' %}
                {% set entries = craft.entries.section('pages') %}
            {% case 'topmenu' %}
                {% set entries = craft.entries.section('pages').showInHeaderMenu('1') %}
            {% case 'footermenu' %}
                {% set entries = craft.entries.section('pages').showInFooterMenu('not 0') %}
            {% case 'descendants' %}
                {% set entries = parent.getChildren() %}
        {% endswitch %}

        {% for page in entries %}
            <!-- macro to get the different urls for different entries...
            we have sometimes links to external pages so you could use page.getUrl() as well -->
            {% set url = _self.getUrl(page) %}
            {% set target = _self.getTarget(page) %}
            {% set title = page.title %}
            {% set class = "" %}

            {% switch page.type.handle %}
                {% case 'internalLink' %}
                    {% set title = page.linkName %}
                {% case 'externalLink' %}
                    {% set title = page.linkName %}
                {% default %}
            {% endswitch %}

            {% if (originalPage.getUrl == url|trim and not page.hasDescendants)  %}
                {% set class = "active" %}
            {% endif %}


            {# Check if entry has descendants #}
            {% if not page.hasDescendants or (menuCategory == "footermenu" or menuCategory == "topmenu") %}
                {# List navigation item if entry has NO child entries #}
                <li class="{{ class }}">
                    <a href="{{ url|trim }}" title="{{ title }}" {{ target }}>{{ title|raw }}</a>
                </li>

            {% elseif menuCategory == "mainmenu" or menuCategory == "descendants" %}
                {# List navigation item if entry does have children #}
                <li>
                    <a href="#" class="{{ class }}" title="{{ title }}">{{ title|raw }}</a>
                    {% set includeParent = (page.type.handle != 'pageWithoutContent')? true : false %}
                    {{ _self.menu('descendants', page, false, level+1, page, originalPage, includeParent ) }}
                </li>
            {% endif %}
        {% endfor %}
    </ul>
{% endmacro %}

Edit

You can use it with

{% import '_macros/menu.twig' as menu %}
<nav id="mainmenu" class="inline">
    {{ menu.menu('mainmenu', entry, true) }}
</nav>

You can remove all the entry.getType().handle checks. We have the entry types pageWithoutContent so they will only appear in the menu as "placeholders" to open sub menus, but if the entry type is a page with content the entry must appear in the sub menu as well to make it clickable for mobile devices. These pages need to have an active when they appear as "parents" but not as "children". You can see a working example of this menu here or here

1
  • Cool thnx! Haven't tried it yet but I'll take your word for it and mark this as the answer for now.
    – Jalen Davenport
    Commented Mar 2, 2018 at 16:23
1

Try wrapping the {% ifchildren %} block with this {% if page.enabled %}.

Full example.

{% nav page in craft.entries.section('pages') %}
<li>
    <a href="{{ page.url }}">{{ page.title }}</a>

    {% if page.enabled %}
        {% ifchildren %}
            <ul>{% children %}</ul>
        {% endifchildren %}
    {% endif %}

</li>
{% endnav %}

PS: I also fixed two typos in your original code.

EDIT:

Perhaps you could filter out the entries that you want, something like this:

{% set entries = craft.entries.section('products').level(1) %}
{{ entries | length | inspect }}
{% set enabledPages = [] %}
{% for entry in entries %}
  {% if entry.enabled %}
    {% set enabledPages = enabledPages | merge([entry]) %}
    {% if entry.hasDescendants %}
      {% set enabledPages = enabledPages | merge(entry.children) %}
    {% endif %}
  {% endif %}
{% endfor %}

{# !! and then the stuff from above without the if page.enabled test #}

{% nav page in craft.entries.section('pages') %}
    <li>
        <a href="{{ page.url }}">{{ page.title }}</a>
        {% ifchildren %}
            <ul>{% children %}</ul>
        {% endifchildren %}
    </li>
{% endnav %}
2
  • thanks for the heads-up on the typos. They're not in the original code lol but I did a little bit of editing when I copy/pasted into SE. So like I mentioned in the question, surrounding any part of the {% nav %} contents gives me the following error: Unexpected "ifchildren" tag (expecting closing tag for the "if" tag defined near line 53). (this is what I get when I use the code you provided above). Any thoughts?
    – Jalen Davenport
    Commented Mar 2, 2018 at 13:51
  • Maybe you could create a new array of entries that only contain the ones that have been enable on the 1st level. Something like this
    – martisj
    Commented Mar 5, 2018 at 11:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.