9

如何将自定义属性添加到 Zend Framework 2 导航中?
我知道我可以添加 id 或 class -> 但仅此而已....

1)例如,我将如何添加data-test='blahblah'属性?
2) 我可以向li包含实际链接的元素添加属性吗?

$container = new Zend\Navigation\Navigation(array(
    array(
        'label' => 'Page 1',
        'id' => 'home-link',
        'uri' => '/',
    ),
    array(
        'label' => 'Zend',
        'uri' => 'http://www.zend-project.com/',
        'order' => 100,
    ),
);

编辑:

@Bram Gerritsen:感谢您的回答。

是的-我可以将其添加'data-test' => 'blahblah'和检索为$page->get('data-test') -但这仍然不会将其作为属性附加到<a></a>...中。我是否应该将htmlify覆盖到那个?

4

3 回答 3

25

Bram 的回答帮助我找到了一个解决方案,这就是我所需要的以及我如何解决它(因为我是 ZF2 和命名空间的新手,所以我花费的时间比它应有的时间长得多,所以希望这对其他人有帮助)

问题

  • 想要使用Zend\Navigation它来受益于它的isActive()方法和内置的翻译、ACL 等支持。
  • 需要将 CSS 类名称添加到<li>元素 <a>元素。(ZF2 的 Menu View Helper 目前支持“非此即彼”的方法)
  • 需要将 CSS 类名称添加到嵌套<ul>元素。
  • 需要向<a>元素添加其他属性,例如data-*="..."
  • 需要这些更改来支持 Bootstrap 3 标记

解决方案说明

  • 通过扩展创建客户视图助手Zend\View\Helper\Navigation\Menu
  • 稍微修改renderNormalMenu()htmlify()方法
  • 利用添加自定义属性的能力为Zend\Pages某些元素添加 CSS 类和附加属性

解决方案

第1步

在 Application 模块下创建自定义 View Helpersrc\Application\View\Helper\NewMenu.php

新菜单.php

<?php
namespace Application\View\Helper;

// I'm extending this class, need to include it
use Zend\View\Helper\Navigation\Menu;

// Include namespaces we're using (from Zend\View\Helper\Navigation\Menu)
use RecursiveIteratorIterator;
use Zend\Navigation\AbstractContainer;
use Zend\Navigation\Page\AbstractPage;


class NewMenu extends Menu
{
    // copied fromZend\View\Helper\Navigation\Menu
    protected function renderNormalMenu(...){} 

    // copied from Zend\View\Helper\Navigation\Menu
    public function htmlify(...){}
}

第2步

getViewHelperConfig()在in 中注册了新的 View Helper\module\Application\Module.php

<?php
/**
 * Zend Framework (http://framework.zend.com/) ...*/

namespace Application;

use Zend\Mvc\ModuleRouteListener;
use Zend\Mvc\MvcEvent;

class Module
{
    // ** snip **

    public function getViewHelperConfig()   {
        return array(
            'invokables' => array(
                // The 'key' is what is used to call the view helper
                'NewMenu' => 'Application\View\Helper\NewMenu',
            )
        );
    }
}

第 3 步

在我的layout.phtml脚本中,我获取了我的 Navigation 容器并将其传递给 NewMenu 视图助手。我还设置了一些选项,例如添加父<ul>类名称而不是转义标签,因此我可以将 Bootstrap 使用的标准“下拉插入符号”(即<b class="caret"></b>)添加到带有下拉菜单的标签中。

$container = $this->navigation('navigation')->getContainer();
echo $this->NewMenu($container)->setUlClass('nav navbar-nav')->escapeLabels(false);

中场休息

此时,我们应该或多或少地复制了 Menu View Helper。它应该以与标准 View Helper 相同的方式生成导航。


第4步

NewMenu.php类中,我删除了$addClassToListItem代码以避免它意外地将类放置在错误的元素上。

受保护的函数 renderNormalMenu(...)

// Add CSS class from page to <li>
//if ($addClassToListItem && $page->getClass()) {
//    $liClasses[] = $page->getClass();
//}

公共函数 htmlify(...)

// Always apply page class to <a> tag. We'll use a diff. method for <li>
//if ($addClassToListItem === false) {
    $attribs['class'] = $page->getClass();
//}

第 5 步

添加一个方法以将 CSS 类名称应用于<li>标签,因为我们删除了该$addClassTolistItem方法。我们只需使用 Page 类的能力来拥有自定义属性并执行此操作:

受保护的函数 renderNormalMenu

// Is page active?
if ($isActive) {
    $liClasses[] = 'active';
}

if($wrapClass = $page->get('wrapClass')){
    $liClasses[] = $wrapClass;
}
...

现在,在我们的 Navigation 配置文件中,我们可以简单地添加一个名为的属性wrapClass,以将 CSS 类应用于包装元素 ( <li>)。

配置\自动加载\global.php

...
'navigation' => array(
    'default' => array(
        ...
        array(
            'label' => 'Products <b class="caret"></b>',
            'route' => 'products',
            'wrapClass' => 'dropdown',         // class to <li>
            'class'     => 'dropdown-toggle',  // class to <a> like usual
            'pages' => array(
                array(
                    'label' => 'Cars',
                    'route' => 'products/type',
                    ...
                ),
                ...
            ),
        ),
...

第 6 步

<a>添加在like上具有附加属性的功能data-*。例如,对于 Bootstrap 3,您将需要data-toggle="dropdown"

公共函数 htmlify(...)

// get attribs for element
$attribs = array(
    'id'     => $page->getId(),
    'title'  => $title,
);

// add additional attributes
$attr = $page->get('attribs');
if(is_array($attr)){
    $attribs = $attribs + $attr;
}

在您的配置文件中,您现在可以添加具有一系列附加属性的属性:

配置\自动加载\global.php

...
'navigation' => array(
    'default' => array(
        ...
        array(
            'label' => 'Products <b class="caret"></b>',
            'route' => 'products',
            'wrapClass' => 'dropdown',         // class to <li>
            'class'     => 'dropdown-toggle',  // class to <a> like usual

            'attribs'   => array(
                'data-toggle' => 'dropdown',  // Key = Attr name, Value = Attr Value
            ),

            'pages' => array(
                array(
                    'label' => 'Cars',
                    'route' => 'products/type',
                    ...
                ),
                ...
            ),
        ),
...

第 7 步

添加将类名放置在嵌套列表容器(即。<ul>)上的功能。

受保护的函数 renderNormalMenu()

if ($depth > $prevDepth) {
    // start new ul tag
    if ($ulClass && $depth ==  0) {
        $ulClass = ' class="' . $ulClass . '"';
    }

    // Added ElseIf below

    else if($ulClass = $page->get('pagesContainerClass')){
        $ulClass = ' class="' . $ulClass . '"';
    }

    else {
        $ulClass = '';
    }
    $html .= $myIndent . '<ul' . $ulClass . '>' . self::EOL;

原始代码基本上是说“如果这是第一个<ul>并且有一个 UL 类,则添加它,否则什么也不做。所以,我添加了一个额外的检查来说明,如果一个名为的属性pagesContainerClass可用,则将该类也应用于该类<ul>

这意味着我们需要在配置中的右侧页面上添加属性:

配置\自动加载\global.php

...
'navigation' => array(
    'default' => array(
        ...
        array(
            'label' => 'Products <b class="caret"></b>',
            'route' => 'products',
            'wrapClass' => 'dropdown',         // class to <li>
            'class'     => 'dropdown-toggle',  // class to <a> like usual

            'attribs'   => array(
                'data-toggle' => 'dropdown',  // Key = Attr name, Value = Attr Value
            ),

            'pages' => array(
                array(
                    'label' => 'Cars',
                    'route' => 'products/type',
                    // Give child <ul> a class name
                    'pagesContainerClass' => 'dropdown-menu',
                    ...
                ),
                ...
            ),
        ),
...

需要注意的重要一点是,UL 类需要放在孩子的第一个子页面上,因为条件语句被包装在以下条件中:

if ($depth > $prevDepth) {
    // start new ul tag
    ...
}

在调用第一个孩子之后, $dept = $prevDepth 和嵌套<ul>将已经被发送到字符串缓冲区。


此解决方案尚未经过严格测试,但其想法是简单地采用当前的 Menu View Helper,并重载两个必要的方法,并且只对其进行轻微修改。

我尝试过使用setPartial(),但这只对<li>生成有所帮助,它仍在使用 Menu View Helpers 的htmlify()方法(所有这些都在 Bram 的上述讨论中提到)。

因此,通过将这些小 tweeks 放入 to 方法并使用 Page 类具有自定义属性的能力,我可以添加一些额外的逻辑来获取和嵌套类上<li>的类名以及在元素上添加额外的属性,所以我可以从配置中配置我以吐出,基本上,Bootstrap 3 Navbar 标记。<a><ul><a>Zend\Navigation

最终布局看起来像这样:

<nav class="navbar navbar-default navbar-static-top" role="navigation">
    <div class="navbar-header">
        <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
        </button>
    </div>
    <div class="collapse navbar-collapse navbar-ex1-collapse">
    <?php
        // Use Zend\Navigation to create the menu
        $container = $this->navigation('navigation')->getContainer();
        echo $this->NewMenu($container)->setUlClass('nav navbar-nav')->escapeLabels(false);
    ?>
    </div><!-- /.navbar-collapse -->
</nav>

我一直遇到的麻烦是更好地理解 PHP 命名空间,并且需要在我的自定义 View Helper 中包含适当的 Qualified 命名空间,即使我正在扩展它。

另一个问题是 Navigation View Helper 可以从自身调用 Menu View Helper,如下所示:

$this->navigation('navigation')->menu();

这不起作用:

$this->navigation('navigation')->NewMenu();

由于命名空间问题,我正在考虑NewMenu未在 Navigation View Helper 类中注册,因此我不会为此扩展它。

因此,希望这个(长)答案能帮助其他正在努力解决这种需求的人。

干杯!

于 2013-09-07T00:06:14.933 回答
10

Page 类有一些用于公共属性(setLabel、等)的专用设置器setIdsetUri如果设置器不存在__set将被调用。有关这方面的更多信息以及关于扩展类的更多信息,请参见手册。AbstractPage

array(
    'label' => 'Page 1',
    'id' => 'home-link',
    'uri' => '/',
    'data-test' => 'blahblah'
),

现在你可以做$page->get('data_test'),它会返回 blahblah。

您的第二个问题是关于更改菜单的呈现(向li.ZF2 添加一个属性是使用菜单视图助手来呈现导航菜单。所有导航视图助手都可以选择使用您自己的部分视图来使用setPartial().

在您的视图中:

$partial = array('menu.phtml', 'default');
$this->navigation()->menu()->setPartial($partial);
echo $this->navigation()->menu()->render();

在您的局部视图menu.phtml中执行以下操作:

<ul>
<?php foreach ($this->container as $page): ?>
    <li data-test="<?=$page->get('data_test')?>"><?=$this->navigation()->menu()->htmlify($page)?></li>
<?php endforeach; ?>
<ul>

这只会呈现菜单的最高级别。如果您有更深/嵌套的结构,您的自定义视图脚本最终会复杂得多。

希望这可以帮助。

于 2013-02-23T10:03:52.840 回答
1

除了jmbertucci评论

问题

标签中的 exсess 插入标记会导致以下问题:

  • 面包屑
  • 菜单翻译

溅射

为防止将标记插入符号添加到标签,您可以在菜单配置中添加对此参数的支持。你应该

src\Application\View\Helper\NewMenu.php

受保护的函数 renderNormalMenu()

/// add 4th parameter $page->get('caret')
$html .= $myIndent . '    <li' . $liClass . '>' . PHP_EOL .
$myIndent . '        ' .
$this->htmlify($page, $escapeLabels, $addClassToListItem, $page->get('caret')) . PHP_EOL;

公共函数 htmlify()

} else {
    $html .= $label;
}
//// add this if
if($caret === true){
    $html .= '<b class="caret"></b>';
}

$html .= '</' . $element . '>';

现在你可以使用它了:

 array(
                'label' => 'Some label',
                'caret' => true,
                'route' => 'someroute',
                'wrapClass' => 'dropdown',
                'class' => 'dropdown-toggle',

附言。jmbertucci,你是男人。

于 2014-05-05T18:49:35.780 回答