How to separate 'normal' child pages from modular children?

HI,

I’m just beginning to learn Grav because I need to port several websites from a dying CMS.

For a drop-down menu I use the approach from the ‘Photographer’ theme as explained in ‘Loop to add children pages as dropdown’.
To detect children, it uses:

{% if p.children.count > 0 %}
                    <ul>
                        {{ _self.loop(p) }}
                    </ul>
                {% endif %}

In my setup I use modular pages and ‘normal’ child pages.
The filter above does take both of them as children, but in my menu I only need the latter ones of course. In the menu on the page the modular children do not appear, but in the source there are empty <ul> </ul> tags.
How can I avoid this?

Edit: code corrected
bigboy

Hoping to bring some more clarity to the question, I have added the file structure, some more explanation and the headers of relevant pages.

File structure:

pages
├── 01.home
│   └── default.md
│
├── 02.eigenschaften      (*1 has "wrong" children)
│   ├── modular.md
│   ├── _anwendung        (*1)
│   │   └── text.md
│   ├── (some more modules)
│   └── _fazit            (*1)
│       └── text.md
│
├── 03.produkte          (*2 has "real" children)
│   ├── default.md
│   │
│   ├── 04.implantate    (*2, has "wrong" children *1)
│   │   ├── modular.md
│   │   ├── _implantat1  (*1)
│   │   │   └── text.md
│   │   ├── (some more modules)
│   │   └── _implantat4  (*1)
│   │       └── text.md
│   │
│   └── 05.werkzeuge     (*2, has "wrong" children *1)
│       ├── modular.md
│       ├── _werkzeug1   (*1)
│       │   └── text.md
│       ├── (some more modules)
│       └── _werkzeug3   (*1)
│           └── text.md
│
├── 04.kontakt
│   └── form.md
│
├── 05.impressum             (*1 has "wrong" children)
│   ├── modular-accodion.md
│   ├── _inhalte             (*1)
│   │   └── accordion.md
│   ├── (some more modules)
│   └── _werbe-mails         (*1)
│       └── accordion.md
│
└── 06.datenschutz           (*1 has "wrong" children)
    ├── modular-accodion.md
    ├── _analyse-tool        (*1)
    │   └── accordion.md
    ├── (some more modules)
    └── _plugins-tools-maps  (*1)
        └── accordion.md

*1 - these are modules of a modular page and should not appear in the dropdown menu and the modular page should not get param ‘submenu’.
*2 - these are children of a page and shall appear in the dropdown menu as 2nd level; the parent page shall get the param ‘submenu’.

Both of them are equally considered to be children of their parent by “p.children.count”.
The “real” ones are shown in the menu in 2nd level with their menu title, the “wrong” ones are not, but their parent is marked as having a submenu.

Two examples of headers:

  • for 03.produkte:
    title: Produkte
    menu: Produkte
    
  • for 04.implantate:
    title: Implantate
    content:
      items: '@self.modular'
      order:
        by: default
        dir: asc
        custom:
          - _implantat1
          - _implantat2
          - _implantat3
          - _implantat4
    menu: Implantate
    

bigboy

@bigboy, Ah, thanks for the updated information. I do get a better picture of the overall situation now.

I think the issue is related in the Twig code. You are looping through all children, while you only want the visible children.
If you’ve only taken the above snippet from the Photographer theme, you may have missed the test {% if p.visible %} in its navigation template.

Try the following:

{% if p.children.visible.count > 0 %}
  <ul>
    {{ _self.loop(p) }}
  </ul>
{% endif %}

If have create a little example using a default Grav installation using Quark.

pages
├── 01.home
│   └── default.md
├── 02.typography
│   └── default.md
└── 03.modular1
    ├── _module1
    │   └── text.md
    ├── _module2
    │   └── text.md
    └── modular.md

Using p.children the menu becomes:

image
Does the above resemble your menu?

Using p.children.visible the menu becomes:

image

No, that test is present as in the Photographer theme.
Here is the full relevant code from the navigation.html.twig partial:

{% macro loop(page) %}
  {% for p in page.children %}
    {% if p.visible %}
      {% set current_page = (p.active or p.activeChild) ? 'active' : '' %}
      {% set menu_level = (p.children.count > 0) ? 'submenu ' : '' %}
      <li class="{{ menu_level }}{{ current_page }}">
        {% if current_page == 'active' %}
          <a href="{{ p.url }}">{{ p.menu }}</a><span class="sr-only">&#160;(aktiv)</span>
        {% else %}
          <a href="{{ p.url }}">{{ p.menu }}</a>
        {% endif %}
        {% if menu_level == 'submenu ' %}
          <ul>
            {{ _self.loop(p) }}
          </ul>
        {% endif %}
      </li>
    {% endif %}
  {% endfor %}
{% endmacro %}

I still don’t understand why that is not working as expected, i.e. every menu item is marked as parent, despite the set visible filter.
Anyway, if I follow your advice and replace the 5th line as follows

 {% set menu_level = (p.children.visible.count) ? 'submenu ' : '' %}

everything is OK! Great!
This allows me to use a pure HTML/CSS multilevel drop-down menu (no JavaScript) in my websites.
Thank you for this “magic”(?) tip.

bigboy