KnpMenuBundle 不适用于 Bootstrap 4 导航栏

KnpMenuBundle not working with Bootstrap 4 navbar

我目前正在使用 Symfony 包制作菜单:KnpMenuBundle。我正在使用 Bootstrap 4 作为样式表。

Bootstrap 4 要求导航栏中的每个列表项都具有 class 'nav-item':

<li class="nav-item active"> <-- this
    <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
</li>

我似乎无法弄清楚如何使用 KnpMenuBundle 将 class 'nav-item' 添加到列表项。目前我在加载页面时看到这个: navbar result

这是我在 src/AppBundle/Menu 中的生成器 class:

namespace AppBundle\Menu;


use Knp\Menu\MenuFactory;

class Builder
{
    public function mainMenu(MenuFactory $factory, array $optioins)
    {
        $menu = $factory->createItem('root');
        $menu->setChildrenAttribute('class', 'navbar-nav mr-auto');
        $menu->addChild('Home', ['route' => 'homepage']);
        $menu->setChildrenAttributes(['class' => 'nav-item']);
        return $menu;
    }
}

我在 base.html.twig 中生成菜单的代码:

{{ knp_menu_render('AppBundle:Builder:mainMenu', {'currentClass': 'active'}) }}

我该怎么做才能让它发挥作用?

您可以在添加时为每个子项目添加一个 class:

$menu->addChild('Home', ['route' => 'homepage'])
     ->setAttributes(array(
         'class' => 'nav-item'
     ));

我这样做是为了让顶级正确。不过还没有制定下拉菜单。

$menu->setChildrenAttribute('class', 'navbar-nav');
// menu items
foreach ($menu as $child) {
    $child->setLinkAttribute('class', 'nav-link')
        ->setAttribute('class', 'nav-item');
}

您应该创建新的模板文件,例如 "extended_knp_menu.html.twig"(另外文件名 不应 命名为 knp_menu.html.twig), 内容 (src):

{% extends 'knp_menu.html.twig' %}

{% macro setCssClassAttribute(item, type, add) %}
    {% set getter = 'get' ~ type %}
    {% set setter = 'set' ~ type %}
    {% set value = attribute(item, getter, ['class']) %}
    {% if value is iterable %}
        {% set value = value|join(' ') %}
    {% endif %}
    {% do attribute(item, setter, ['class', value ~ ' ' ~ add]) %}
{% endmacro %}

{% block item %}
    {% import "knp_menu.html.twig" as macros %}
    {#
    As multiple level is not currently supported by bootstrap 4
    This requires you to install
    https://github.com/bootstrapthemesco/bootstrap-4-multi-dropdown-navbar
    And set the the use_multilevel = true
    #}
    {% set use_multilevel = false %}

    {% if item.displayed %}
        {%- set attributes = item.attributes %}
        {%- set is_dropdown = attributes.dropdown|default(false) %}
        {%- set divider_prepend = attributes.divider_prepend|default(false) %}
        {%- set divider_append = attributes.divider_append|default(false) %}

        {# unset bootstrap specific attributes #}
        {%- set attributes = attributes|merge({'dropdown': null, 'divider_prepend': null, 'divider_append': null }) %}

        {%- if divider_prepend %}
            {{ block('dividerElement') }}
        {%- endif %}

        {# building the class of the item #}
        {%- set classes = item.attribute('class') is not empty ? [item.attribute('class'), 'nav-item'] : ['nav-item'] %}
        {%- if matcher.isCurrent(item) %}
            {%- set classes = classes|merge([options.currentClass]) %}
        {%- elseif matcher.isAncestor(item, options.depth) %}
            {%- set classes = classes|merge([options.ancestorClass]) %}
        {%- endif %}
        {%- if item.actsLikeFirst %}
            {%- set classes = classes|merge([options.firstClass]) %}
        {%- endif %}
        {%- if item.actsLikeLast %}
            {%- set classes = classes|merge([options.lastClass]) %}
        {%- endif %}

        {# building the class of the children #}
        {%- set childrenClasses = item.childrenAttribute('class') is not empty ? [item.childrenAttribute('class')] : [] %}
        {%- set childrenClasses = childrenClasses|merge(['menu_level_' ~ item.level]) %}

        {# adding classes for dropdown #}
        {%- if is_dropdown %}
            {%- set classes = classes|merge(['dropdown']) %}
            {%- set childrenClasses = childrenClasses|merge(['dropdown-menu']) %}
        {%- endif %}

        {# putting classes together #}
        {%- if classes is not empty %}
            {%- set attributes = attributes|merge({'class': classes|join(' ')}) %}
        {%- endif %}
        {%- set listAttributes = item.childrenAttributes|merge({'class': childrenClasses|join(' ') }) %}

        <li{{ macros.attributes(attributes) }}>
            {# displaying the item #}
            {%- if is_dropdown %}
                {{ block('dropdownElement') }}
            {%- elseif item.uri is not empty and (not item.current or options.currentAsLink) %}
                {{ block('linkElement') }}
            {%- else %}
                {{ block('spanElement') }}
            {%- endif %}
            {%- if divider_append %}
                {{ block('dividerElement') }}
            {%- endif %}
            {% if item.hasChildren and options.depth is not same as(0) and item.displayChildren %}
                {{ block('dropdownlinks') }}
            {% endif %}
        </li>
    {% endif %}
{% endblock %}

{% block dropdownlinks %}
    {% if use_multilevel %}
        <ul class="dropdown-menu">
    {% else %}
        <div class="dropdown-menu">
    {% endif %}
    {% for item in item.children %}
        {{ block('renderDropdownlink') }}

        {% if use_multilevel and item.hasChildren and options.depth is not same as(0) and item.displayChildren %}
            {{ block('dropdownlinks') }}
        {% endif %}
    {% endfor %}
    {% if not use_multilevel %}
        </div>
    {% else %}
        </ul>
    {% endif %}
{% endblock %}

{% block renderDropdownlink %}
    {% import _self as ownmacro %}
    {%- set divider_prepend = item.attributes.divider_prepend|default(false) %}
    {%- set divider_append = item.attributes.divider_append|default(false) %}
    {%- set attributes = item.attributes|merge({'dropdown': null, 'divider_prepend': null, 'divider_append': null }) %}

    {% if use_multilevel %}
        <li>
    {% endif %}

    {%- if divider_prepend %}
        {{ block('dividerElementDropdown') }}
    {%- endif %}

    {%- if item.uri is not empty and (not item.current or options.currentAsLink) %}
        {{ ownmacro.setCssClassAttribute(item, 'LinkAttribute', 'dropdown-item') }}
        {{ block('linkElement') }}
    {%- else %}
        {{ block('spanElementDropdown') }}
    {%- endif %}

    {%- if divider_append %}
        {{ block('dividerElementDropdown') }}
    {%- endif %}

    {% if use_multilevel %}
        </li>
    {% endif %}
{% endblock %}

{% block spanElementDropdown %}
    {% import "knp_menu.html.twig" as macros %}
    {% import _self as ownmacro %}
    {{ ownmacro.setCssClassAttribute(item, 'LabelAttribute', 'dropdown-header') }}
    <div {{ macros.attributes(item.labelAttributes) }}>
        {% if item.attribute('icon') is not empty  %}
            <i class="{{ item.attribute('icon') }}"></i>
        {% endif %}
        {{ block('label') }}
    </div>
{% endblock %}

{% block dividerElementDropdown %}
    <div class="dropdown-divider"></div>
{% endblock %}

{% block dividerElement %}
    {% if item.level == 1 %}
        <li class="divider-vertical"></li>
    {% else %}
        <li class="divider"></li>
    {% endif %}
{% endblock %}

{% block linkElement %}
    {% import "knp_menu.html.twig" as macros %}
    {% import _self as ownmacro %}
    {{ ownmacro.setCssClassAttribute(item, 'LinkAttribute', 'nav-link') }}
    <a href="{{ item.uri }}"{{ macros.attributes(item.linkAttributes) }}>
        {% if item.attribute('icon') is not empty  %}
            <i class="{{ item.attribute('icon') }}"></i>
        {% endif %}
        {{ block('label') }}
    </a>
{% endblock %}

{% block spanElement %}
    {% import "knp_menu.html.twig" as macros %}
    {% import _self as ownmacro %}
    {{ ownmacro.setCssClassAttribute(item, 'LabelAttribute', 'navbar-text') }}
    <span {{ macros.attributes(item.labelAttributes) }}>
        {% if item.attribute('icon') is not empty  %}
            <i class="{{ item.attribute('icon') }}"></i>
        {% endif %}
        {{ block('label') }}
    </span>
{% endblock %}

{% block dropdownElement %}
    {% import "knp_menu.html.twig" as macros %}
    {%- set classes = item.linkAttribute('class') is not empty ? [item.linkAttribute('class')] : [] %}
    {%- set classes = classes|merge(['dropdown-toggle', 'nav-link']) %}
    {%- set attributes = item.linkAttributes %}
    {%- set attributes = attributes|merge({'class': classes|join(' ')}) %}
    {%- set attributes = attributes|merge({'data-toggle': 'dropdown'}) %}
    <a href="#"{{ macros.attributes(attributes) }}>
        {% if item.attribute('icon') is not empty  %}
            <i class="{{ item.attribute('icon') }}"></i>
        {% endif %}
        {{ block('label') }}
        <b class="caret"></b>
    </a>
{% endblock %}

{% block label %}{{ item.label|trans }}{% endblock %}

仅包含菜单渲染模板后:

{{ knp_menu_render('AppBundle:Builder:mainMenu', {
    'currentClass': 'active', 
    'template': 'extended_knp_menu.html.twig'
}) }}

希望对你有所帮助。