I hope I have understand your problem..
Let's look at your code:
One sidenote: It might be good to limit your pages
-selection only to active pages:
{% set pages = craft.entries.section('about').level(1).status('live') %}
First of all, store the elements of the slug in an array:
{% set slugs = craft.request.segments %}
Here is the first for-loop for the first-level elements:
<ul class="side-nav p-0">
{% for page in pages %}
{% set activePage = (page.slug in slugs) %}
<li class="{% if activePage %} active{% endif %}">
<a href="{{page.url}}">{{ page.title }} </a>
</li>
{% endfor %}
</ul>
The important part here is to check whether the slug of the page in the for-loop is active. In your code, you checked the slug of the entry (of the currently active page, not the page in the loop) with the slug of the request. This will always yield a true result, which is what you experienced.
Using the code above you will by now receive a list of all first-level pages, the active one with labelled with class="active"
.
Now let's look into the second loop for the subpages.
{% set activeSubPage = (entry.level == 2 and entry.getParent().slug == page.slug) %}
{% if activePage or activeSubPage %}
{% set subPage = craft.entries.descendantOf(page) %}
<ul class="p-0 pl-5">
{% for child in subPage %}
<li><a href="{{child.url}}">{{child.title}}</a></li>
{% endfor %}
</ul>
{% endif %}
This time it is not enough to check whether the slug of the active page in the loop matches the slug of the actual page (this will show children only if the parent page is selected), we also need to check whether the parent's slug matches the active page (to show children even if the active page is one of the children itself and not the parent).
With activeSubPage
we check at first, whether the page is level 2 (therefore it has a parent and we won't run into an error for an undefined attribute on pages that are level 1 and do not have a parent) and if so, whether the parent's slug is equal to the slug of the current page in the loop.
We enclose the second for-loop with an if-clause that checks both of the results, whether the current item in the loop is either activePage
or activeSubPage
. The remaining code is identical to your code.
You could now check again, whether the sub-item is active, by using the same principle: {% if (child.slug in slugs) %}active{% endif %}
Here is the complete updated code:
{% set pages = craft.entries.section('about').level(1).status('live') %}
{% set slugs = craft.request.segments %}
<ul class="side-nav p-0">
{% for page in pages %}
{% set activePage = (page.slug in slugs) %}
<li class="{% if activePage %} active{% endif %}">
<a href="{{page.url}}">{{ page.title }} </a>
{% set activeSubPage = (entry.level == 2 and entry.getParent().slug == page.slug) %}
{% if activePage or activeSubPage %}
{% set subPage = craft.entries.descendantOf(page) %}
<ul class="p-0 pl-5">
{% for child in subPage %}
<li><a href="{{child.url}}">{{child.title}}</a></li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
I hope this helps and solves your problem. If you have any questions or if you do not understand my explanation (sorry for my bad English, I'm not a native speaker): Don't hesitate to ask!
Further improvements:
With some small improvements you could put this code into a file and use it recursively for navigations with more than two levels. In your template, you would then be able to include it like this
{% include "_includes/nav_links_withchildren" with { 'slug' : 'about' } %}
and would then include itself again to list sub-pages.