1

I am creating a Structure for the main navigation that has two possible choices for the Link: Entries field or a Categories field. The answer I received here showed me how to setup the basics but if the Category page I want to link to is a Sub Category, it will only link to the Parent.

{% set menu = craft.entries.section('navDropDownMenu').all() %}
<ul>
    {% nav link in menu %}
        <li>
        {% if link.entriesURL|length %}
            {% if craft.app.request.absoluteUrl ==  link.entriesFieldURL[0].url %}
                <a href="{{ link.entriesFieldURL[0].url }}" class="active">{{ link.title }}</a>
            {% else %}
                <a href="{{ link.entriesFieldURL[0].url }}">{{ link.title }}</a>
            {% endif %}
        {% else %}
            {% if craft.app.request.absoluteUrl ==  link.categoryFieldURL[0].url %}
                <a href="{{ link.categoryFieldURL[0].url }}" class="active">{{ link.title }}</a>
            {% else %}
                <a href="{{ link.categoryFieldURL[0].url }}">{{ link.title }}</a>
            {% endif %}
        {% endif %} 
            {% ifchildren %}
                <ul>
                    {% children %}
                </ul>
            {% endifchildren %}
        </li>
    {% endnav %}

So what I'm trying to figure out is, if I have a category setup like this:

-Shoes
    -Tennis
    

in the context of using the Nav Tag (as above), how do I get the link to 'Tennis' and not 'Shoes'?

I know you can output a nav of Categories, but I wanted to use a Structure if possible because the navigation has a mixture of categories and links to internal pages, and they are mixed together in the navigation as far as sorting goes.

1 Answer 1

2

In short, don't use the {% nav %} tag as it's not flexible enough for what you need.

Assuming your fields are called entriesFieldURL and categoriesFieldURL, try something like this:

{# Fetch the current page ID to use for your active class later on. If entry exists, we are on an entry page, else, category page or NULL fallback #}
{% set currentPageId = entry.id ?? category.id ?? null %}

{# Fetch your entries and eager-load children #}
{% set menu = craft.entries()
    .section('navDropDownMenu')
    .level(1)
    .with('children')
    .all() %}

<ul>
{%- for item in menu -%}
    
    {# Set a link object which switches based on whether you have a category or an entry #}
    {%- set link = item.entriesFieldURL|length ? item.entriesFieldURL[0] : item.categoriesFieldURL.leaves()[0] -%}
    <li>
        
        {# Output the link and add active class base on the current page when needed #}
        <a href="{{ link.url }}" class="{{ link.id == currentPageId ? 'active' }}">{{ link.title }}</a>
        
        {# Does this item have children? #}
        {%- if item.children -%}
        <ul>
            {%- for item in item.children -%}
            
            {# As above, set a link object which switches based on whether you have a category or an entry #}
            {%- set link = item.entriesFieldURL|length ? item.entriesFieldURL[0] : item.categoriesFieldURL.leaves()[0] -%}
            <li>
                
                {# And again, output the link and add active class when needed #}
                <a href="{{ link.url }}" class="{{ link.id == currentPageId ? 'active' }}">{{ link.title }}</a>
            </li>
            {%- endfor -%}
        </ul>
        {%- endif -%}
    </li>
{%- endfor -%}
</ul>

As you can see, there's some repetition in there which you could solve by using a short macro to build the <a> tags making the complete thing:

{# Fetch the current page ID to use for your active class later on. If entry exists, we are on an entry page, else, category page or NULL fallback #}
{% set currentPageId = entry.id ?? category.id ?? null %}

{# Fetch your entries and eager-load children #}
{% set menu = craft.entries()
    .section('navDropDownMenu')
    .level(1)
    .with('children')
    .all() %}

{% macro linkItem(item, currentPageId) %}
    {%- set link = item.entriesFieldURL|length ? item.entriesFieldURL[0] : item.categoriesFieldURL.leaves()[0] -%}
    <a href="{{ link.url }}" class="{{ link.id == currentPageId ? 'active' }}">{{ link.title }}</a>
{% endmacro %}

<ul>
{%- for item in menu -%}
    <li>
        {{ _self.linkItem(item, currentPageId) }}
        {%- if item.children -%}
        <ul>
            {%- for item in item.children -%}
                <li>
                    {{ _self.linkItem(item, currentPageId) }}
                </li>
            {%- endfor -%}
        </ul>
        {%- endif -%}
    </li>
{%- endfor -%}
</ul>

You could further refine but hopefully, this gets you started :)

8
  • This has the same problem, it can't handle sub-categories (it will only link to the top level). Feb 1, 2021 at 18:25
  • Do you mean if you choose a subcategory in your categoriesFieldURL?
    – Oli
    Feb 2, 2021 at 10:33
  • No, what I'm saying is, if I have a Category Field, and the category I choose is a subcategory (like my example Shoe > Tennis) Then what appears is only the Parent category, which I don't want in that case. Put another way, I'm accessing product pages by category, so what I need from a Navigation element is to link to the Parent as well as Sub categories, depending on the nav item. My idea with this Structure was that I could have some Nav items that are Entries and Some that are Categories (parent and sub), but only the Parent outputs. Feb 2, 2021 at 19:12
  • I just re-read your question and I think I misunderstood. Yes, if the choice is a sub-category, it ignores that choice and outputs the Parent. Feb 2, 2021 at 20:43
  • 1
    I changed the code in the answer, could you give that another go?
    – Oli
    Feb 3, 2021 at 7:24

Your Answer

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

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