Find on which item route from the menu I am - symfony

I'm currently working on a Symfony 4.4 web application.
The menu can be changed by the administrators of my app, so there is a table with these properties :
Because I got routes with some parameters (eg: a slug or an id, and the slug can change so it's not a great identifier).
The problem is that I want to know on which menu item I am, to keep the menu opened (to add an active class on < li > items) but I can't find a way to do it properly.
What I tried :
Identify the route with something like that :
<li>{% if app.request.get('_route') == 'foo_products_overview' and app.request.get('slug') in ["entityslug"] %} class="active" {% endif %}></li>
But parameters are not the same for each route of my app (mutliple entites using id or slug to find one).
Here is the way my menu items are displayed :
{% if child2.getMenuItems()|length > 0 %}
<ul class="collapse nav-sub" aria-expanded="false">
{% for child3 in child2.getMenuItems() if child2.getMenuItems()|length > 0 and child3.level == 4 and (is_granted(child3.roles) or child3.roles is empty) %}
<li class="{% if child3.getMenuItems()|length > 0 %}nav-dropdown{% endif %}">
<a class="{% if child3.getMenuItems()|length > 0 %}has-arrow{% endif %}"
href="{% if child3.route is not null %}{% if child3.routeParameters %}{{ path(child3.route, {'id': child3.routeParameters}) }}{% else %}{{ path(child3.route) }}{% endif %}{% else %}#{% endif %}"
aria-expanded="false">
<span>{% if "ROLE_ADMIN" in child3.roles %}<i class="la la-eye-slash text-danger mr-2"></i>{% endif %}{{ child3.name|raw }}</span>
</a>
</li>
{% endfor %}
</ul>
{% endif %}
Have you ever had this problem?
Maybe there's a way with KnpMenu? (https://symfony.com/bundles/KnpMenuBundle/current/index.html) I'm not using it for the moment.

Did you try this : replace "child.id" by your variable name, i think that's "child3.id" but not sure.
class="{% if app.request.attributes.get( '_route' ) starts with 'foo_products_overview' and app.request.attributes.get('id') == child.id %}active{% endif%}"

Related

Django style active element url in for loop

{% for i in menu_categories %}
<a href="{% url 'category' i.slug %}"
class="mr-4 font-light{% if i.slug == url_name %}text-emerald-500 text-3xl{% endif %} hover:text-emerald-600">
{{ i.title }}
</a>
{% endfor %}
I was looking for similar questions, but I didn’t check the url in the for loop.
What's wrong?

How to get current page in pages Symfony 5 without duplicate code?

I want get the current page and give an active class without duplicate code in symfony 5.
Here is the code example :
<li class="{{ app.request.get('_route_')=='home' ? 'active':''}}">
Home
</li>
<li class="{{ app.request.get('_route_')=='contact' ? 'active':''}}">
Contact
</li>
</ul>
As an extension of the answer of #DhiaDjobbi
I've you really want to reduce "duplicate" code, I'd go with defining the menu in an array as well
{% set active_page = app.request.get('_route') %}
{% for page in pages %}
<li {% if page == active_page %} class="active" {% endif %} >
{{ page }}
</li>
{% endfor %}
U can create a twig variable in Template its better readable.
{% set page = app.request.get('_route') %}
Then use If Condition to test.
<li {% if page == 'home' %} class="active" {% endif %} >
Home
</li>
<li {% if page == 'contact' %} class="active" {% endif %} >
Contact
</li>

Setting current category to active

When on a category page I'm hoping to add an "active" class the current pages relevant link to show which category is selected in the menu.
Currently I'm listing all post categories and linking to their relevant pages via
{% for cat in categories %}
<li><a style="margin: 0;" href="{{ cat.link }}" class="">{{cat.name}}</a></li>
{% endfor %}
I have tried using if statements but can't seem to get it right.
Managed to solve this by setting the current category to active like this:
{% for cat in categories %}
<li><a style="margin: 0;" href="{{ cat.link }}" class="menu-item {% if category|capitalize == cat.name %}active{% endif %}">{{cat.name}}</a></li>
{% endfor %}

Django CMS menu, how to set a class to parents only?

My menu needs some work. I need to have a class on the parent menu items, but with my recursion, it is not working.
This is (part of) my menu:
Home
Teachers (id teachers)
Contact
Info
Projects
myproject
yourproject
I start with "teachers" like this:
{% show_menu_below_id "teqchers" 0 1 0 1 "teachers_menu.html" %}
And this is my teachers_menu.html:
{% load menu_tags %}
{% for child in children %}
<li class="{% if child.selected %}selected parent_{{forloop.counter}}{% endif %} {% if child.sibling %}parent_{{forloop.counter}} {% endif %}">
{{ child.get_menu_title }}
{% if child.children %}
<div class="submenu">
<ul>
{% show_menu from_level to_level extra_inactive extra_active template "" "" child %}
</ul>
</div>
{% endif %}
</li>
{% endfor %}
With this in place, my menu is working for a bit.
When i click Projects, all is well and the 2 projects are in view. But when i click a project, i expect the page to show, but it does not, it does rebuild my menu, and adds the needed class to the child elements:
{% if child.selected %}selected parent_{{forloop.counter}}{% endif %}
Obvious because it is a child now i guess, but how to prevent this? I only need that class for the first menu items.
Instead of doing this:
{% show_menu from_level to_level extra_inactive extra_active template "" "" child %}
I now added another template like this:
{% show_menu from_level to_level extra_inactive extra_active "teachers_submenu.html" "" "" child %}
And in that template I now have:
{% load menu_tags %}
{% for child in children %}
<li class="{% if child.selected %}selected{% endif %}">
{{ child.get_menu_title }}
</li>
{% endfor %}
So the extra template takes care of the submenu. With some styling it now works.

Django CMS Multi-Level Dropdown Menu

Im kinda new to Django CMS and im trying my best to avoid asking, but this one drives me crazy.
I made a Wiki app with a Topic, and Category model. I hooked it to a Site on my CMS and added it to my Menu. Now i would like to be able to show all Top-Level categories, their Child Categories & Topics, and the Child categories of these, and so on, on my menu.
Menu/Navigation should look like this:
Wiki
Category1
Category1.1
Topic
Category1.2
Topic
Category2
Topic
Category3
...
Right now i can only show the Top categories:
Wiki
Category1
Category2
Category3
I Already created a menu.py to get a Custom SubMenu on my Wiki (the one you see above):
menu.py
class WikiSubMenu(CMSAttachMenu):
name = _("Wiki Sub-Menu")
def get_nodes(self, request):
nodes = []
categories = Category.objects.filter(parent_id__isnull=True)
for c in categories:
node = NavigationNode(
mark_safe(c.name),
c.get_absolute_url(),
c.id,
)
nodes.append(node)
return nodes
menu_pool.register_menu(WikiSubMenu)
My Category Model:
class Category(models.Model):
''' Category model. '''
name = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
description = models.TextField(blank=True)
parent = models.ForeignKey(
'self',
null=True,
blank=True,
related_name='children'
)
sort = models.IntegerField(default=0)
class Meta:
ordering = ['sort', 'name']
def __unicode__(self):
return self.name
#models.permalink
def get_absolute_url(self):
return ('topics:categories_category_detail', (), {'slug': self.slug})
def get_all_children(self):
return Category.objects.filter(parent=self)
Now, is it possible to create a Sub-SubMenu, for all Categories with Childs, and their Childs, and their Childs, and so on?
Thanks for help & sorry for bad english
-- EDIT : --
I just found that:
docs.django-cms.org/en/3.0.6/extending_cms/app_integration.html#integration-modifiers
(Removed direct link to add 2 new Links, sorry for that)
I think that is what im looking for, i was kinda blind that i didn't found it. I'll try it out and Post the Answer if it worked out.
-- EDIT (AGAIN): --
The modifier didn't worked for me, but i got a whole piece further,
i read the Docs again, and found that i can give the NavigationNodes an optional attr dictonary, which i filled with all Categories with parent=c, on that way i had the data i needed, then i found that real nice bootstrap dropdown menu, that does exacly what i wanted. So my code until now looks like that:
menu.py
class TopicsSubMenu(CMSAttachMenu):
name = _("Wiki Sub-Menu")
def get_nodes(self, request):
nodes = []
categories = Category.objects.filter(parent_id__isnull=True)
for c in categories:
node = NavigationNode(
mark_safe(c.name),
c.get_absolute_url(),
c.pk,
attr=dict(
subcategories=Category.objects.filter(parent=c),),
)
nodes.append(node)
return nodes
And my Template:
menu.html
{% for child in children %}
<li>
{% if child.children %}
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
{{ child.get_menu_title }}
<span class="caret">
</span>
</a>
<ul class="dropdown-menu multi-level" role="menu" aria-labelledby="dropdownMenu">
{% for child in child.children %}
{% if child.attr.subcategories.count %}
<li class="dropdown-submenu">
<a tabindex="-1" href="#">{{ child.get_menu_title }}</a>
<ul class="dropdown-menu">
{% for subcategory in child.attr.subcategories %}
<li>
<a tabindex="-1" href="{{ subcategory.get_absolute_url }}">{{ subcategory }}</a>
</li>
{% endfor %}
</ul>
</li>
{% else %}
<li>{{ child.get_menu_title }}</li>
{% endif %}
{% endfor %}
</ul>
{% else %}
<a href="{{ child.get_absolute_url }}">
<span>
{{ child.get_menu_title }}
</span>
</a>
{% endif %}
</li>
{% if class and forloop.last and not forloop.parentloop %}
{% endif %}
{% endfor %}
My next step will be to write the whole "for" loops from the template in a Method, make it recursive with a while loop or something and post the result as Answer.
I hope i can help someone with that stuff :)
WOHO! I finally did it!
base.html
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
{% show_menu 0 100 100 100 "menu.html" %}
</ul>
</div>
menu.html:
{% for child in children %}
<li class="child{% if child.selected %} selected{% endif %}{% if child.ancestor %} ancestor{% endif %}{% if child.sibling %} sibling{% endif %}{% if child.descendant %} descendant{% endif %}{% if child.children %} dropdown{% endif %}">
<a {% if child.children %}class="dropdown-toggle" data-toggle="dropdown"{% endif %} href="{{ child.attr.redirect_url|default:child.get_absolute_url }}">
<span>{{ child.get_menu_title }}</span>{% if child.children|length %}<span class="caret"></span>{% endif %}
</a>
{% if child.children %}
<ul class="dropdown-menu multi-level" role="menu" aria-labelledby="dropdownMenu">
{% show_menu from_level to_level extra_inactive extra_active "dropdownmenu.html" "" "" child %}
</ul>
{% endif %}
</li>
{% if class and forloop.last and not forloop.parentloop %}{% endif %}
{% endfor %}
and my dropdownmenu.html:
(The recursive stuff starts here)
{% load i18n menu_tags cache mptt_tags %}
{% for child in children %}
<li {% if child.children %}class="dropdown-submenu"{% else %} {% endif %}>
<a tabindex="-1" href="{{ child.attr.redirect_url|default:child.get_absolute_url }}">{{ child.get_menu_title }}</a>
{% if child.children %}
<ul class="dropdown-menu">
{% show_menu from_level to_level extra_inactive extra_active "dropdownmenu.html" "" "" child %}
</ul>
{% endif %}
</li>
{% endfor %}
and the most important, menu.py:
class TopicsSubMenu(CMSAttachMenu):
name = _("Wiki Sub-Menu")
def get_nodes(self, request):
nodes = []
categories = Category.objects.all()
for c in categories:
node = NavigationNode(
mark_safe(c.name),
c.get_absolute_url(),
c.pk
)
if c.parent:
node.parent_id = c.parent_id
nodes.append(node)
topics = Topic.objects.all().exclude(category__isnull=True)
for t in topics:
node = NavigationNode(
mark_safe(t.title),
t.get_absolute_url(),
t.pk,
t.category.all()[0].id,
parent_namespace="TopicsSubMenu"
)
nodes.append(node)
return nodes
menu_pool.register_menu(TopicsSubMenu)
And thats it!

Resources