
KnpMenu - JSON Renderer to support Dojo Dijit/Tree
I wrote some code that parsed through the default KNP menu HTML and created a Dijit menu. It worked, but it was slow and awkward.
I couldn't find an easy way to get the KNP menu in the JSON form that would work well with Dijit/Tree, so I wrote my own.
JsonRenderer.php
PHP
<?php | |
| |
namespace AppBundle\Menu; | |
| |
use Knp\Menu\ItemInterface; | |
use Knp\Menu\Matcher\MatcherInterface; | |
use Knp\Menu\Renderer\RendererInterface; | |
use Translator; | |
| |
class JsonRenderer implements RendererInterface | |
{ | |
| |
/** | |
* @var \Twig_Environment | |
*/ | |
private $environment; | |
private $matcher; | |
private $defaultOptions; | |
| |
/** | |
* @param \Twig_Environment $environment | |
* @param string $template | |
* @param MatcherInterface $matcher | |
* @param array $defaultOptions | |
*/ | |
public function __construct( \Twig_Environment $environment, $template, MatcherInterface $matcher, array $defaultOptions = array() ) | |
{ | |
$this->environment = $environment; | |
$this->matcher = $matcher; | |
$this->defaultOptions = array_merge( array( | |
'depth' => null, | |
'matchingDepth' => null, | |
'currentAsLink' => true, | |
'currentClass' => 'current', | |
'ancestorClass' => 'current_ancestor', | |
'firstClass' => 'first', | |
'lastClass' => 'last', | |
'template' => $template, | |
'compressed' => false, | |
'allow_safe_labels' => false, | |
'clear_matcher' => true, | |
'leaf_class' => null, | |
'branch_class' => null | |
), $defaultOptions ); | |
| |
} | |
| |
public function render( ItemInterface $item, array $options = array() ) | |
{ | |
$options = array_merge( $this->defaultOptions, $options ); | |
| |
$translator = $options['translator']; | |
| |
$itemIterator = new \Knp\Menu\Iterator\RecursiveItemIterator( $item ); | |
| |
$iterator = new \RecursiveIteratorIterator( $itemIterator, \RecursiveIteratorIterator::SELF_FIRST ); | |
| |
$items = []; | |
foreach( $iterator as $item ) | |
{ | |
$translatedLabel = $translator->trans($item->getLabel()); | |
$id = $item->getName(); | |
$parentId = $item->getParent()->getName(); | |
$itemData = [ 'id' => strtolower( $item->getName() ), 'name' => $translatedLabel, 'uri' => $item->getUri()]; | |
if ($parentId !== $id) { | |
$itemData['parent'] =strtolower($parentId); | |
} | |
$itemData['has_children'] = $item->hasChildren(); | |
$items[] = $itemData; | |
} | |
$lastItem = count( $items ) - 1; | |
$items[$lastItem]['lastItem'] = true; | |
| |
$html = $this->environment->render( $options['template'], array('items' => $items, 'options' => $options, 'matcher' => $this->matcher) ); | |
| |
if( $options['clear_matcher'] ) | |
{ | |
$this->matcher->clear(); | |
} | |
return $html; | |
} | |
| |
} |
Added JsonRenderer as a service in services.yml
Code
app.menu_renderer: | |
# The class implements Knp\Menu\Renderer\RendererInterface | |
class: AppBundle\Menu\JsonRenderer | |
arguments: [ "@twig", "knp_menu.html.twig", "@knp_menu.matcher", {"translator": "@translator" }] | |
tags: | |
# The alias is what is used to retrieve the menu | |
- { name: knp_menu.renderer, alias: json } |
I used a twig template (knp_menu.html.twig) to output the JSON, perhaps in the future I will update it to deliver it as a .js file.
Code
var menuTreeStoreData = [{% for item in items%}{{item|json_encode()|raw}}{% if item.lastItem is not defined %},{%endif%}{% endfor %}]; |
The layout template calls the renderer with this line:
Code
{{ knp_menu_render('admin',{'template': 'admin/parts/knp_menu.html.twig'}, 'json') }} |
Finally, this is menu.js which uses the menu data produced by JsonRenderer to create a nice tree for a menu. You could use a different menu approach. I kind of like the tree for now.
Code
define([ | |
"dojo/dom", | |
"dojo/store/Memory", | |
"dijit/tree/ObjectStoreModel", | |
"dijit/Tree", | |
"dojo/domReady!" | |
], function (dom, | |
Memory, ObjectStoreModel, Tree) { | |
//"use strict"; | |
function run() { | |
var store = new Memory({data: menuTreeStoreData | |
, | |
getChildren: function (object) { | |
return this.query({parent: object.id}); | |
}}); | |
var model = new ObjectStoreModel({ | |
store: store, | |
query: {id: 'admin'} | |
}); | |
// Create the Tree. | |
var tree = new Tree({ | |
id: "admin-menu", | |
model: model, | |
persist: true, | |
onClick: function (item) { | |
if( typeof item.uri !== "undefined" && item.uri !== null ) { | |
location.href = item.uri; | |
} | |
}, | |
getIconClass: function (item, opened) { | |
return (item && item.has_children) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "dijitLeaf" | |
} | |
}); | |
tree.placeAt(dom.byId("admin-left-menu")); | |
tree.startup(); | |
} | |
return { | |
run: run | |
}; | |
}); |
Print article | This entry was posted by elvis on 10/04/16 at 08:18:00 pm . Follow any responses to this post through RSS 2.0. |