How to display the class for the active menu? - css

Here is the menu.twig.html template provided by Drupal Bootstrap :
{#
/**
* #file
* Default theme implementation to display a menu.
*
* Available variables:
* - classes: A list of classes to apply to the top level <ul> element.
* - dropdown_classes: A list of classes to apply to the dropdown <ul> element.
* - menu_name: The machine name of the menu.
* - items: A nested list of menu items. Each menu item contains:
* - attributes: HTML attributes for the menu item.
* - below: The menu item child items.
* - title: The menu link title.
* - url: The menu link url, instance of \Drupal\Core\Url
* - localized_options: Menu link localized options.
*
* #ingroup templates
*
* Define a custom macro that will render all menu trees.
*/
#}
{% macro menu_links(items, attributes, menu_level, classes, dropdown_classes) %}
{% if items %}
<ul{{ attributes.addClass(menu_level == 0 ? classes : dropdown_classes) }}>
{% for item in items %}
{%
set item_classes = item.url.getOption('container_attributes').class | split(" ")
%}
{%
set item_classes = [
item.is_expanded and item.below ? 'expanded dropdown',
item.in_active_trail ? 'active active-trail',
loop.first ? 'first',
loop.last ? 'last',
]
%}
<li{{ item.attributes.addClass(item_classes) }}>
{% set link_title = item.title %}
{% set link_attributes = item.link_attributes %}
{% if menu_level == 0 and item.is_expanded and item.below %}
{% set link_title %}{{ link_title }} <span class="caret"></span>{% endset %}
{% set link_attributes = link_attributes.addClass('dropdown-toggle').setAttribute('data-toggle', 'dropdown') %}
{% endif %}
{# Must use link() here so it triggers hook_link_alter(). #}
{{ link(link_title, item.url, link_attributes.addClass(item.in_active_trail ? 'active-trail')) }}
{% if item.below %}
{{ _self.menu_links(item.below, attributes.removeClass(classes), menu_level + 1, classes, dropdown_classes) }}
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% endmacro %}
{#
Invoke the custom macro defined above. If classes were provided, use them.
This allows the template to be extended without having to also duplicate the
code above. #see http://twig.sensiolabs.org/doc/tags/macro.html
#}
{{ _self.menu_links(items, attributes, 0, classes ?: ['menu', 'menu--' ~ menu_name|clean_class, 'nav'], dropdown_classes ?: ['dropdown-menu']) }}
Here is the main menu HTML code :
<ul class="menu menu--main nav navbar-nav">
<li class="first">
Banques
</li>
<li>
Cashback
</li>
<li class="active active-trail">
Avis
</li>
<li>
Essentiel
</li>
<li class="last">
Actu
</li>
</ul>
I want to customize this menu and add an icon before each menu link and the number of page elements in a badge.
Here is the custom code I want for my main menu :
<ul class="menu menu--main nav navbar-nav">
<li>
<i class="fas fa-piggy-bank fa-lg"></i> Banques <span class="badge badge-light">{{ drupal_view_result('accueil_banque', 'page_1')|length }}</span>
</li>
<li>
<i class="fas fa-undo fa-lg"></i> Cashback <span class="badge badge-light">{{ drupal_view_result('accueil_cashback', 'page_1')|length }}</span>
</li>
<li>
<i class="fas fa-gift fa-lg"></i> Avis <span class="badge badge-light">{{ drupal_view_result('accueil_avis', 'page_1')|length }}</span>
</li>
<li>
<i class="fas fa-thumbs-up fa-lg"></i> Essentiel <span class="badge badge-light">{{ drupal_view_result('accueil_essentiel', 'page_1')|length }}</span>
</li>
<li>
<i class="fas fa-newspaper fa-lg"></i> Actu <span class="badge badge-light">{{ drupal_view_result('accueil_actu', 'page_1')|length }}</span>
</li>
</ul>
The problem is that when I click on a menu link, the menu is not displayed as active.
How can I have the same behavior in my custom code ? With the active class automatically added to the menu link that is currently displayed.
How else can I integrate my custom code into one of the menu.html.twig or menu--main.html.twig templates ?
In Bootstrap there is a menu--main.html.twig template that I copy to my sub-theme, but I don't know how to use it. Here are its contents :
{#
/**
* #file
* Default theme implementation to display a menu.
*
* Available variables:
* - classes: A list of classes to apply to the top level <ul> element.
* - dropdown_classes: A list of classes to apply to the dropdown <ul> element.
* - menu_name: The machine name of the menu.
* - items: A nested list of menu items. Each menu item contains:
* - attributes: HTML attributes for the menu item.
* - below: The menu item child items.
* - title: The menu link title.
* - url: The menu link url, instance of \Drupal\Core\Url
* - localized_options: Menu link localized options.
*
* #ingroup templates
*/
#}
{% extends "menu.html.twig" %}
{%
set classes = [
'menu',
'menu--' ~ menu_name|clean_class,
'nav',
'navbar-nav',
]
%}
Example of the main menu by default with an open link (it is active because the page is displayed) :
Example of the main menu personalized with an open link (it is not active, because the classes are not automatically added by the template menu.twig.html) :

The answer they gave you in the referenced issue is a very clear one RTFM (Read the f***ing manual) especially the following:
core/themes/stable/templates/menu.html.twig
core/themes/classy/templates/menu.html.twig contains {% extends "menu.html.twig" %} (to add a specific class) and expects Stable's menu.html.twig to be extended
core/themes/YOUR_DIR/templates/menu.html.twig contains {% extends "menu.html.twig" %} (to override a Twig block) and expects Classy's menu.html.twig to be extended — or, really, any of its ancestor themes, because it couldn't care less if Classy's menu.html.twig was removed
Notes:
in point 3, we specifically do NOT want to specify {% extends "#classy/menu.html.twig" %} because we don't want to care. We just want the parent theme's template — according to the theme registry — to be extended.
in point 1, the "root template" could just as well not live in a theme, but in a module. That's also a valid use case.
So you have an issue because you state menu.twig.html but your module extends "menu.html.twig" so make sure your naming is right.
The Drupal handbook statesNEVER to fiddle around with the delivered templates, rather extend it with user specific modulues.
Q: Where should I make changes?
A: In a custom sub-theme
You should never modify any theme or sub-theme that is packaged and
released from Drupal.org. If you do, all changes you have made would
be lost once that theme is updated. This makes keeping track of
changes next to impossible.
Instead, you should create a custom sub-theme that isn't hosted on
Drupal.org.

Related

Drupal add extra fields to menu

I have some issue with drupal 9 and the menu. Basically i want to add some extra fields for menu link, like icon (image or svg upload) and some text for description. I use Simplify menu for the menus.
I already tried with Menu item extras module but i can't print the added fields in twig file.
Someone know how to fix this and get the fields value in twig template? (menu.html.twig)?
Thanks
You can print it in *.html.twig file, the below code I rendered the main menu like:
{% set items = simplify_menu('main') %}
{{ menu_item.NEW_FIELD }}
<nav class="nav">
<ul class="navbar-ul">
{% for menu_item in items.menu_tree %}
<li class="nav-item">
<a class="nav-link" href="{{ menu_item.url }}">{{ menu_item.text }}</a>
</li>
{% endfor %}
</ul>
</nav>

Reuse attach menu and prevent selection issues in Django-cms

Ive developed an navigation bar and needed an way to attach an menu to some pages, but if i reuse the menu and attach it on more than one page all dropdowns gets opened if one of the attached navigation nodes gets selected.
currently i was filtering the nodes in the template, so i think the menu determinate the seletions by all nodes and not only by the shown.
Navigation template (line 219 - 237)
<div class="ci-evo-select">
<div class="hover h-100 w-100 pos-rel">
<a class="h-100 w-100" href="{{ child.get_absolute_url }}">
<span class="p-5vh-lr">{{ child.title }}{% if show_id %}{{ child.id }}{% endif %}</span>
</a>
<div class="dropdown w-100 pos-abs pos-bot-left ci-evo-weiß text-evo-block {% if child.selected %}show{% endif %}">
{% for drop in child.children %}
{% if drop.get_absolute_url == child.get_absolute_url %}
{# no child nodes #}
<div class="ci-evo-select hover">
<a class="w-100" href="{{ child.get_absolute_url }}#{{ drop.attr.identifier }}">
<span >{{ drop.title }}</span>
</a>
</div>
{% endif %}
{% endfor %}
</div>
</div>
</div>
cms_menus.py (complete)
from menus.menu_pool import menu_pool
from menus.base import NavigationNode
from django.utils.translation import ugettext_lazy as _
from cms.menu_bases import CMSAttachMenu
from . import models
class AnchorAttachMenu(CMSAttachMenu):
name = _("AttachMenu")
def get_nodes(self, request):
nodes = [
# static nodes goes here
]
anchors = models.AnchorHookModel.objects.all()
for anchor in anchors:
n = NavigationNode(anchor.name, anchor.page.get_absolute_url(), anchor.parent.pk)
n.attr["identifier"] = anchor.identifier
n.selected = False
nodes.append(n)
return nodes
menu_pool.register_menu(AnchorAttachMenu)
is there a way to get the navigation node where the menu gets attached?

Drupal 8 remove extra divs and add custom classes in menu

Currently the {{page.primary_menu}} created the extra divs and default d8 classes as below:
<div class="region region-primary-menu">
<nav role="navigation" aria-labelledby="block-demo-main-menu-menu" id="block-demo-main-menu">
<h2 class="sr-only" id="block-demo-main-menu-menu">Main navigation</h2>
<ul class="menu menu--main nav navbar-nav">
<li class="first">
Home
</li>
<li>
Home
</li>
<li class="last">
ABOUT US
</li>
</ul>
</nav>
</div>
However, I want to generate the menu structure as like:
<ul id="top-menu" class="nav navbar-nav navbar-right mu-main-nav">
<li>HOME</li>
<li>ABOUT US</li>
<li>MENU</li>
<li>RESERVATION</li>
<li>GALLERY</li>
<li>OUR TEAM</li>
<li>BLOG</li>
<li>CONTACT</li>
</ul>
I've created a file name demo.theme and pasted the code but it did not give me the expected result.
<?php
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Template\Attribute;
/**
* Implements hook_preprocess_HOOK() for HTML document templates.
*
* Adds body classes if certain regions have content.
*/
function demo_menu_tree(&$variables) {
return '<ul id="top-menu" class="nav navbar-nav navbar-right mu-main-nav">' . $variables['tree'] . '</ul>';
}
Any suggestion?
Make sure you have twig debugging enabled, it will make your life a lot easier, by adding comments to your mark up (which you can see inline in the web inspector). Using those comments you can figure out what you should name your theme file.
Create a new custom twig file in the /templates directory of your theme like so themes/[your-theme-name-here]/templates/menu.html.twig. As a starting point I'd suggest either using the default classy theme menu.html.twig template, or clone use the file referenced inline in the markup comments of your site when you have twig debugging enabled.
Edit the menu.html.twig file to meet your needs, something like this:
{% import _self as menus %}
{#
We call a macro which calls itself to render the full tree.
#see http://twig.sensiolabs.org/doc/tags/macro.html
#}
{{ menus.menu_links(items, attributes, 0) }}
{% macro menu_links(items, attributes, menu_level) %}
{% import _self as menus %}
{% if items %}
{% if menu_level == 0 %}
<ul id="top-menu" {{ attributes.addClass('nav navbar-nav navbar-right mu-main-nav') }}>
{% else %}
<ul class="menu">
{% endif %}
{% for item in items %}
{%
set classes = [
'menu-item',
item.is_expanded ? 'menu-item--expanded',
item.is_collapsed ? 'menu-item--collapsed',
item.in_active_trail ? 'menu-item--active-trail',
]
%}
<li{{ item.attributes.addClass(classes) }}>
{{ link(item.title, item.url) }}
{% if item.below %}
{{ menus.menu_links(item.below, attributes, menu_level + 1) }}
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% endmacro %}

Add new Button in the List view of sonata admin Bundle

Maybe the question is too easy to answer, but I searched in the documentation of sonata admin bundle and i didn't find what I need.
When you navigate to the List view of a model in sonata admin Bundle you find the Action Button in the upper right and under it you find the add new action.
In my case I need that the Add new action to be displayed directly In the View Like in this screenshot :
Any one can help me please ?
I know this is an old question, but I'll leave this answer for future reference.
I had to do something similar, this is how I did it.
In your admin class override the configureActionButtons() method
class YourAdmin extends AbstractAdmin
{
//...
/**
* Overriden from (AbstractAdmin)
*/
public function configureActionButtons($action, $object = null)
{
$list = parent::configureActionButtons($action,$object);
$list['custom_action'] = array(
'template' => 'AcmeBundle:YourAdmin:custom_button.html.twig',
);
return $list;
}
//...
}
And then creating your button in AcmeBundle/Resources/views/YourAdmin/custom_button.html.twig
{# custom_button.html.twig #}
<a class="sonata-action-element" href="{{ admin.generateUrl('your_route') }}">
<i class="fa fa-plus-circle"></i>Custom Action
</a>
Of course you can add permissions check afterwards (they were omitted for clarity)
Hope it helps somebody
In SonataAdmin ~3 you can configure the templates that display the template for your action directly in the YourEntityAdmin::configureListFields() method.
To do so you first
create new template anywhere, extend sonata layout and use sonata_admin_content block.
"Anywhere" really means "anywhere": this confused me a bit at first.
Basically, you create a template for example in Resources\CRUD\list__action_your_action.html.twig and then call it from the configuration in the YourEntityAdmin::configureListFields():
class YourEntityAdmin
{
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
// other fields...
->add('_action', null, array(
'actions' => array(
// ...
'clone' => array(
'template' => 'AppBundle:CRUD:list__your_action.html.twig'
)
)
))
;
}
}
REMEMBER TO WRITE SOMETHING IN THIS TEMPLATE, ALSO A SIMPLE STRING LIKE
{# Resources\CRUD\list__action_your_action.html.twig #}
your action button template
"your action button template" will be rendered in the "Actions" column and is basically the label of your button: using the template you can manipulate it as you like.
This were really hard to understand as in my column there were no actions as I leaved the template empty so not rendering anything!
Anyway, you can find the full procedure to add a new button (and an associated route/action) in the documentation about CREATING A CUSTOM ADMIN ACTION.
Which Sonata version are you on ? And what is the version of your example?
Mine is 2.3, does not look like you can play on that from parameters, but you can override the layout to let only appear the create button.
If you need it for all your admins, you better override the layout from the config. If you need it only for list, or show, or remove only overrides those templates then.
in config.yml:
sonata_admin:
templates:
layout: AppBundle:Layouts:standard_layout_override.html.twig
show: AppBundle:Layouts:show.html.twig
list: AppBundle:Layouts:list.html.twig
delete: AppBundle:Layouts:delete.html.twig
edit: AppBundle:Layouts:edit.html.twig
in that file override that block:
{% block sonata_page_content_header %}
{% block sonata_page_content_nav %}
{% if _tab_menu is not empty or _actions is not empty %}
<nav class="navbar navbar-default" role="navigation">
{% block tab_menu_navbar_header %}
{% if _navbar_title is not empty %}
<div class="navbar-header">
<span class="navbar-brand">{{ _navbar_title|raw }}</span>
</div>
{% endif %}
{% endblock %}
<div class="container-fluid">
<div class="navbar-left">
{% if _tab_menu is not empty %}
{{ _tab_menu|raw }}
{% endif %}
</div>
{% if _actions|replace({ '<li>': '', '</li>': '' })|trim is not empty %}
<ul class="nav navbar-nav navbar-right">
<li class="dropdown sonata-actions">
{{ 'link_actions'|trans({}, 'SonataAdminBundle') }} <b class="caret"></b>
<ul class="dropdown-menu" role="menu">
{{ _actions|raw }}
</ul>
</li>
</ul>
{% endif %}
</div>
</nav>
{% endif %}
{% endblock sonata_page_content_nav %}
{% endblock sonata_page_content_header %}
You should also be able to do it for some admins only overriding defining a specific template for those admins on service definitions like that:
app.admin.product:
class: AppBundle\Admin\ProductAdmin
arguments: [~, AppBundle\Entity\Product, AppBundle:Admin\Product]
tags:
- {name: sonata.admin, manager_type: orm, group: Products, label: Products}
calls:
- [ setTemplate, [edit, AppBundle:Product:edit.html.twig]]
But I can't get that "AppBundle:Product:edit.html.twig" template to delete the list action overriding the same block.
Hope this helps you.

Rendering different controller view in sitelayout in symfony 2

I have a layout which has a sidebar. Inside sidebar, there is a block for displaying list of categories. I have called a controller (fetches list of categories) to be rendered inside this block. Here is how my sidebar looks like:
Here is my main layout file containing sidebar:
<!-- siteLayout.html.twig -->
<div id="sidebar">
{% block sidebarBlock1 %}
{% render "TestBundle:Index:categoryList" %}
{% endblock %}
</div>
TestBundle:Index:categoryList fetches list of categories from database and returns as below:
<ul>
<li>Category 1</li>
<li>Category 2</li>
</ul>
All my other views extends siteLayout.html.twig. What i want is that when users loads this url "/category/1" i want to add css class to li tag.
For example if someone clicks /category/1 then the output should be
<li class="active">.......</li>
How can I achieve this?
<li {% if app.request.attributes.get('_route') == 'category_view' %}
class="active"
{% endif %}>
</li>
Check route and set class if route matches your category route. Replace category_view with the route name of /category
{%if app.request.server.get('REQUEST_URI')== path('viewCategoryItems', {'slugName': category.slugName})%} class="active"{%endif%}
This worked for me

Resources