10

In admin/structure/menu I created a menu. Is it possible to render this menu (with machinename: my_menu) in a twig template? Somehow like:

{{ my_menu }}

4 Answers 4

14

Simplify Menu sounds like what you'd want.

The simplify_menu module uses a TwigExtension to gain access to Drupal's main menu's (or any other menu for that matter), render array so it can be accessed from a twig template. Among the many advantages of having full control of the menu's render array in a twig template is the ability to customize the markup for your menus to ensure they are accessible and comply with standards.

// Get menu items
{% set items = simplify_menu('main') %}

// Iterate menu tree
<ul>
  {% for menu_item in items.menu_tree %}
    <li class="{{ item_class }} navigation__item">
      <a href="{{ menu_item.url }}" class="{{ link_class }}">{{ menu_item.text }}</a>
    </li>
  {% endfor %}
</ul>

Otherwise, there are a handful of menu-* twig templates to use when rendering a menu from a block. Turn on your Twig debugging settings in Drupal to see what the suggestions are.

However, you need to have pretty good knowledge of menu theming to get it exactly right (like handling child items and not flat menu structure as the example above displays) - that said, I would stick to letting a block render out the menu in a given region. Then, inspect menu.html.twig from your base theme (either classy or stable) to get an idea of what your options are if you want to modify markup.

Be sure to use a decent twig template suggestion and not just menu.html.twig - as that will affect every menu that gets rendered by Drupal... it is the default template.

I would also suggest to install Menu Block as that will grant extra flexibility in theming and twig file suggestions. It also makes rendering trees of a menu (like section based navigation in a sidebar) a breeze.

13
  • thanks. The reason why I asked my question above is because I am not able to render my menu (my block menu) in a nested twig template? See here my question. Maybe you have the answer..?
    – meez
    Jan 14, 2017 at 18:22
  • Simplify Menu lets you call any menu from any twig template it looks like.
    – Kevin
    Jan 14, 2017 at 18:24
  • I installed and enabled Simplify Menu. In my header.twig file I added {% set items = simplify_menu('main') %} but (in this case) the Main navigation menu is not rendered?
    – meez
    Jan 14, 2017 at 21:31
  • Did you follow the rest of the example? You either have to do it that way, or call a block that holds a menu, render it and create a variable. It's best to leave it to the block system, honestly, instead of inject menus into other twig templates they wouldn't normally be in.
    – Kevin
    Jan 14, 2017 at 21:47
  • ok thanks If I follow the rest of the example it's working! But like you suggest, it's better to leave it to the block system. How do I do this with Simplify Menu? 1) In admin/structure/block I placed the menu in a region and then I disabled it (so now I have the Block ID). 2) In my header.twig I tried {{ simplify_menu('crmnavigation') }}, but it's not loading the block? Do you know how do I load the block and the menu within it in my header.twig using Simplify Menu?
    – meez
    Jan 15, 2017 at 7:05
10

For those who are still looking for an alternative approach, I suggest using Twig Tweak module as a more stable and more global use case, check out its cheat sheet

For rendering the menu via twig tweak, you can just do the following:

{# Expand menu items to display the entire menu tree. #}    
{{ drupal_menu('menu_machine_name') }}

{# Specify menu level and depth. #}
{{ drupal_menu('admin', 2, 3) }}
4

Enable the simplify_menu module, then add the code below to your page.html.twig or other twig template where you would like to render your menu:

<nav class="navigation__main" role="navigation">
  {% set item_class = 'navigation__item' %}
  {% set link_class = 'navigation__link' %}
  <ul class="navigation__items">
    {% set items = simplify_menu('main') %}
      {% for menu_item in items.menu_tree %}
          <li class="{{ item_class }}"><a href="{{ menu_item.url }}" class="{{ link_class }}">{{ menu_item.text }}</a></li>
      {% endfor %}
  </ul>
</nav>

the 'main' is the menu id which you can find by hovering over the menu's edit button in the menus page and looking at the url displayed at the bottom of the browser.

0

If you are sick of installing modules for basic things, call this function in a preprocess and add the return value into variables and you can then use it in twig:

 /**
 * Helper function to get menu items as array for a given menu id/name
 *
 * @param String $menu_id
 *
 * @return Array menu items array with url, title properties
 */
function getArrayFromMenu($menu_id) {
   // Load menu as array to render in twig file
   $tree = \Drupal::menuTree()->load($menu_id, new MenuTreeParameters());
   $menu_array = [];

   if (!$tree) {
      return $menu_array;
   }

   // Sort according to the weight. @see https://www.drupal.org/project/drupal/issues/3019572
   $menu_tree = \Drupal::service('menu.link_tree');
   $manipulators = array(
      array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort'),
   );

   $tree = $menu_tree->transform($tree, $manipulators);
   foreach ($tree as $item) {
      $title = $item->link->getTitle();
      /** @var \Drupal\Core\Url */
      $url_obj = $item->link->getUrlObject();
      $menu_array[] = array(
         'title' => $title,
         'url' => $url_obj->toString()
      );
   }

   return $menu_array;
}

It will just do the complex work, you just need to pass the menu id. In my case, menu id was mapped with a field of custom block type.

Your Answer

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

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