How would I add custom attributes into Zend Framework 2 navigation?
I know I can add id or class -> but that\'s about it....
1) How would I add data-test=\'bla
Bram's answer helped point me to a solution, here's what I needed and how I solved it (since I was new to ZF2 and namespaces it took me much longer than it should have, so hopefully this will help others)
Problem
Zend\Navigation
to benefit from its isActive()
method and the built in translation, ACL, etc support.
element and
element. (ZF2's Menu View Helper supports an 'either or' approach currently)
elements.
element such as data-*="..."
Solution Description
Zend\View\Helper\Navigation\Menu
renderNormalMenu()
and htmlify()
methodsZend\Pages
to add CSS classes and additional attributes to some elementsSolution
Step 1
Created custom View Helper under the Application module src\Application\View\Helper\NewMenu.php
NewMenu.php
Step 2
Registered new View Helper with the getViewHelperConfig()
in \module\Application\Module.php
array(
// The 'key' is what is used to call the view helper
'NewMenu' => 'Application\View\Helper\NewMenu',
)
);
}
}
Step 3
In my layout.phtml
script, I get my Navigation container and pass it to the NewMenu view helper. I also set some options like adding the parent
class name and not escaping labels so I can add the standard 'dropdown caret' that Bootstrap uses (ie. ) to a label with a dropdown menu.
$container = $this->navigation('navigation')->getContainer();
echo $this->NewMenu($container)->setUlClass('nav navbar-nav')->escapeLabels(false);
Intermission
At this point, we should have more or less just duplicated the Menu View Helper. It should produce a navigation the same way the standard View Helper does.
Step 4
In the NewMenu.php
class, I remove the $addClassToListItem
code to avoid it from placing classes on the wrong element by accident.
protected function renderNormalMenu(...)
// Add CSS class from page to -
//if ($addClassToListItem && $page->getClass()) {
// $liClasses[] = $page->getClass();
//}
public function htmlify(...)
// Always apply page class to tag. We'll use a diff. method for -
//if ($addClassToListItem === false) {
$attribs['class'] = $page->getClass();
//}
Step 5
Add a method to apply CSS class name to tags, since we removed the
$addClassTolistItem
method. We simply use the Page classes ability to have custom properties and do this:
protected function renderNormalMenu
// Is page active?
if ($isActive) {
$liClasses[] = 'active';
}
if($wrapClass = $page->get('wrapClass')){
$liClasses[] = $wrapClass;
}
...
Now, in our Navigation config file, we can simply add a property called wrapClass
to apply CSS classes to the wrapping element ().
config\autoload\global.php
...
'navigation' => array(
'default' => array(
...
array(
'label' => 'Products ',
'route' => 'products',
'wrapClass' => 'dropdown', // class to -
'class' => 'dropdown-toggle', // class to like usual
'pages' => array(
array(
'label' => 'Cars',
'route' => 'products/type',
...
),
...
),
),
...
Step 6
Add the ability to have additional attributes on like
data-*
. For Bootstrap 3 you'll need data-toggle="dropdown"
for example.
public function 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;
}
In your config file, you can now add a property with an array of additional attributes:
config\autoload\global.php
...
'navigation' => array(
'default' => array(
...
array(
'label' => 'Products ',
'route' => 'products',
'wrapClass' => 'dropdown', // class to -
'class' => 'dropdown-toggle', // class to like usual
'attribs' => array(
'data-toggle' => 'dropdown', // Key = Attr name, Value = Attr Value
),
'pages' => array(
array(
'label' => 'Cars',
'route' => 'products/type',
...
),
...
),
),
...
Step 7
Add the ability to place class names on nested lists container (ie.
).
protected function 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 . '' . self::EOL;
The original code basically said "if this is the first
and there's a UL class, add it, else do nothing. So, I added an additional check to say, if a property called pagesContainerClass
is available, to apply the class to the
as well.
This means we need to add the property on the right Page in our configuration:
config\autoload\global.php
...
'navigation' => array(
'default' => array(
...
array(
'label' => 'Products ',
'route' => 'products',
'wrapClass' => 'dropdown', // class to -
'class' => 'dropdown-toggle', // class to like usual
'attribs' => array(
'data-toggle' => 'dropdown', // Key = Attr name, Value = Attr Value
),
'pages' => array(
array(
'label' => 'Cars',
'route' => 'products/type',
// Give child
a class name
'pagesContainerClass' => 'dropdown-menu',
...
),
...
),
),
...
Important to note, the UL class needs to be placed on the first child Page of a child because the conditional statements are wrapped in a the following condition:
if ($depth > $prevDepth) {
// start new ul tag
...
}
After the first child is called, the $dept = $prevDepth and the nested
will have already been sent to the string buffer.
This solution hasn't been rigorously tested but the idea is that is simply takes the current Menu View Helper, and overloads the two necessary methods and only slightly modifies that.
I've tried to use setPartial()
but that only helped with the generation, it was still using the Menu View Helpers'
htmlify()
method (all of which was mentioned in Bram's discussion above).
So with making those small tweeks to the to methods and using the Page class's ability to have custom properties, I could just add some additional logic to get class names on the ,
and nested
classes as well as add additional properties on the elements, so I could configure my
Zend\Navigation
from the config to spit out, basically, Bootstrap 3 Navbar markup.
The end Layout then just looks like this:
The troubles I kept running into was a better understanding of PHP Namespaces and having needed to include the appropriate Qualified namespaces in my custom View Helper, even though I was extending it.
The other problem, was that the Navigation View Helper can call the Menu View Helper from itself like so:
$this->navigation('navigation')->menu();
This won't work:
$this->navigation('navigation')->NewMenu();
I'm thinking because of namespace issues with NewMenu
not being registered in the Navigation View Helper class and I'm not going to extend it just for that.
So, hopefully this (long) answer will help others who are struggling with this need.
Cheers!