【问题标题】:ZF2 custom attributes in navigation导航中的 ZF2 自定义属性
【发布时间】:2013-02-23 04:52:33
【问题描述】:

如何将自定义属性添加到 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 覆盖到那个?

【问题讨论】:

    标签: zend-framework2


    【解决方案1】:

    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模块src\Application\View\Helper\NewMenu.php下创建自定义View Helper

    NewMenu.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 步

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

    <?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 脚本中,我获取了我的导航容器并将其传递给NewMenu 视图助手。我还设置了一些选项,例如添加父 &lt;ul&gt; 类名而不是转义标签,这样我就可以将 Bootstrap 使用的标准“下拉插入符号”(即&lt;b class="caret"&gt;&lt;/b&gt;)添加到带有下拉菜单的标签中。

    $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 类名应用到 &lt;li&gt; 标签的方法,因为我们删除了 $addClassTolistItem 方法。我们只需使用 Page 类的能力来拥有自定义属性并执行此操作:

    受保护的函数 renderNormalMenu

    // Is page active?
    if ($isActive) {
        $liClasses[] = 'active';
    }
    
    if($wrapClass = $page->get('wrapClass')){
        $liClasses[] = $wrapClass;
    }
    ...
    

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

    config\autoload\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 步

    添加在&lt;a&gt; 上具有其他属性的功能,例如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;
    }
    

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

    config\autoload\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 步

    添加在嵌套列表容器中放置类名的功能(即&lt;ul&gt;)。

    受保护的函数 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;
    

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

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

    config\autoload\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 和嵌套的&lt;ul&gt; 将已经被发送到字符串缓冲区。


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

    我尝试使用setPartial(),但这只对&lt;li&gt; 一代有帮助,它仍在使用菜单视图助手的htmlify() 方法(所有这些都在 Bram 的上述讨论中提到)。

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

    最后的 Layout 看起来就像这样:

    <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 类中注册的命名空间问题,因此我不会为此扩展它。

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

    干杯!

    【讨论】:

      【解决方案2】:

      Page 类有一些用于公共属性的专用设置器(setLabelsetIdsetUri 等),如果设置器不存在,__set 将被调用。有关此内容以及扩展 AbstractPage 类的更多信息,请参阅 manual

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

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

      您的第二个问题是关于更改菜单的呈现(向li 添加属性。ZF2 正在使用menu view helper 呈现导航菜单。 所有导航视图助手都可以选择使用您自己的局部视图进行渲染,使用 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>
      

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

      希望这会有所帮助。

      【讨论】:

      • 感谢您的回答。是的 - 我可以添加 'data-test' => 'blahblah' 并将其检索为 $page->get('data-test') - 但这仍然不会将其作为属性附加到 ....我是否必须将 htmlify 重写为?
      • 是的,您需要扩展菜单视图助手,并且只覆盖 htmlify 方法。查看 htmlify 方法,您将看到定义的属性数组。您可以在那里添加您的自定义属性。请参阅我的answer 关于如何注册自己的导航视图助手的另一个 SO 问题。
      • 感谢您的回复。所以 - 我的菜单实际上有 2 个深度......所以本质上 - 使用部分视图 - 我必须在容器上递归迭代 - 对吗?我不能只是简单地遍历页面项目...
      • 没错。您可以使用RecursiveIteratorIterator,查看menu view helper 中的renderMenu() 方法以获取如何实现此功能的线索。如果您不定义自定义部分,则此方法是默认渲染。
      • 由于您引用的文档不是 $page-&gt;getDataTest() 而是 $page-&gt;get( 'data-test' ) 有效。请修正你的答案。
      【解决方案3】:

      除了jmbertucci评论

      问题

      标签中的插入符号会导致以下问题:

      • 面包屑
      • 菜单翻译

      拼接

      要防止将标签 caret 添加到标签,您可以在菜单配置中添加对该参数的支持。你应该

      前往

      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',
      

      ps 。 jmbertucci,你就是男人。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-04-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多