Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rootAttributes rendering option #166

Open
dirkluijk opened this issue Jul 21, 2014 · 14 comments
Open

Add rootAttributes rendering option #166

dirkluijk opened this issue Jul 21, 2014 · 14 comments

Comments

@dirkluijk
Copy link

Hi,

I want to set the root attributes of a menu from inside a Twig template. I am working on a project where different themes need to have different CSS classes for the menu, and it seems quite ugly to override the MenuBuilder just to change the childAttributes of the root item.

My suggestion is to add a rootAttributes option to the default renderers.

Example usage (Twig renderer):

{# render Bootstrap nav #}
{{ knp_menu_render('main', {
    'currentClass': 'active',
    'rootAttributes': { class: 'nav navbar-nav' }
}) }}

If you agree, I could submit a PR with all necessary changes to the default renderers, docs and twig template.

@dirkluijk dirkluijk changed the title Add rootAttributes render option Add rootAttributes rendering option Jul 21, 2014
@Nek-
Copy link
Contributor

Nek- commented Jul 23, 2014

👍

@Nek- Nek- added RFC and removed Improvement labels Jul 24, 2014
@jeremylivingston
Copy link

👍

I've searched up and down for a way to add a class to the root element from knp_menu_render, but come up with nothing. Is this truly not possible currently?

It seems like this should be a standard feature of the library. If this capability does not exist, I would definitely encourage the addition of a rootAttributes option.

I'd be happy to do the work to accomplish this, if necessary.

@dcarbone
Copy link

It seems the main issues are here:

This means that even if you define a Builder as such:

$menu = $factory->createItem('root', $options);

 $menu->setAttribute('class', 'root-class');
 $menu->setAttribute('id', 'root_id');
 $menu->setChildrenAttribute('class', 'child-class');

... the $menu->setAttribute() call is utterly ignored, instead passing an array of the children's attributes into:

I assume this was done to enable root-level setting of child attributes, and it can be overcome fairly easily with a custom renderer or by extending the base twig template. If this is not the reason why, I am definitely curious to hear it.

@stof
Copy link
Collaborator

stof commented Jan 29, 2015

This is because attributes are for the <li> tag and childrenAttributes are for the <ul> tag with the list of children. For the root node, there is no <li> tag, but we decided to keep consistency by always using childrenAttributes for the <ul> even at the root.

@dcarbone
Copy link

@stof: Ah excellent! Thank you for the clarification. Is this mentioned somewhere in documentation that I missed?

@stof
Copy link
Collaborator

stof commented Jan 29, 2015

@dcarbone
Copy link

Thank you, sorry for commenting on such an old issue.

@aur1mas
Copy link

aur1mas commented Jun 24, 2015

wether problem was solved? because I have same use case as @dirkluijk mentioned. And from the code I see that is impossible to pass attributes from twig.

@stof
Copy link
Collaborator

stof commented Jun 24, 2015

@aur1mas knp_menu_render options are renderer options. These options will not alter the menu being rendered in any way (I will keep rejected any PR trying to do this). There are already several way to influence the menu attributes:

  • changing the children attributes of the root menu from the template:

    {% set menu = knp_menu_get('main') %}
    {% do menu.setChildrenAttribute('class', 'child-class') %}
    {% knp_menu_render(menu, {'activeClass': 'active'}) %}
  • passing an option to the menu builder and use it in the builder to change the way the menu is built

    {% set menu = knp_menu_get('main', options={'root_class': 'child-class'}) %}
    {% knp_menu_render(menu, {'activeClass': 'active'}) %}
    class MenuBuilder
    {
        // ...
    
        public function buildMainMenu(array $options)
        {
            $menu = $this->factory->createItem('main', array(
                'childrenAttributes' => array(
                    'class' => isset($options['root_class']) ? $options['root_class'] : null,
                ),
            ));
    
            // Continue building the menu
    
            return $menu;
        }
    }

Note that the second way does not work when registering menu themselves as services (which has several drawbacks, including this one). You need to use either the convention-based provider (AppBundle:MenuBuilder:buildMainMenu here) or the new way to register builders as service once KnpLabs/KnpMenuBundle#273 will be merged and released (hint: very soon)

@Seb33300
Copy link

Seb33300 commented Mar 3, 2017

👍 Need an option to override the root class from renderer function

@stof
Copy link
Collaborator

stof commented Mar 3, 2017

I gave 2 solutions just above, which don't require new features (and don't require adding new responsibilities in the renderer)

@martixy
Copy link

martixy commented Dec 5, 2017

I myself tried to do this just now.
I was setting the attributes option on the root component of my menu(the ul), and kept wondering why its not working.
After looking at the code I discovered that the it doesn't render from attributes, but from childrenAttributes for some reason.

{% block root %}
{% set listAttributes = item.childrenAttributes %}
{{ block('list') -}}
{% endblock %}
{% block list %}
{% if item.hasChildren and options.depth is not same as(0) and item.displayChildren %}
{% import _self as knp_menu %}
<ul{{ knp_menu.attributes(listAttributes) }}>
{{ block('children') }}
</ul>
{% endif %}
{% endblock %}

I have 2 questions:

  1. What are attributes used for?
  2. What does the children part of childrenAttributes refer to? Children of what?

IMO documenting the ItemInterface at least would be a good idea.

@stof
Copy link
Collaborator

stof commented Feb 9, 2018

@martixy the answer to this question is given in a previous comment in this discussion: #166 (comment)

@bennukem
Copy link

bennukem commented Nov 9, 2020

{% set menu = knp_menu_get('main') %} {% do menu.setChildrenAttribute('class', 'child-class') %} {% knp_menu_render(menu, {'activeClass': 'active'}) %}

If menu as a service and need to be used with two different ways, by exemple one render with class on ul and do nothing on the other render. do menu.setChildrenAttribute() will work on the service and no on only the display.

Actually the only way is to make a template for each render with special need... :(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants