- be created with dynamic structure from backend
- be responsive, i.e. desktop- and mobile-friendly
- have submenu items with navigation links
- support touch events
- support keyboard accessibility
- they are not really responsive
- submenu items only collapse / expand the submenus and can not contain navigation links
- ...
But how to output the dynamic menu structure? ui:repeat is not a choice here because the structure (nested sub menus, etc.) is not known a priori. Fortunately, there is OmniFaces with o:tree, which allows to have full control over the markup of a tree hierarchy by declaring the JSF components or HTML elements in the markup. o:tree does not render any HTML markup by itself. Exactly what I need!
I ended up with this XHTML fragment mixing o:treeNode, o:treeNodeItem, o:treeInsertChildren and HTML elements defined by the mentioned FlexNav menu:
<h:outputScript library="js" name="jquery.flexnav.js"/>
<h:outputStylesheet library="css" name="flexnav.css"/>
<ul id="mainnavi" class="flexnav" data-breakpoint="640" role="navigation">
<o:tree value="#{mainNavigationBean.treeModel}" var="item">
<o:treeNode level="0">
<o:treeNodeItem>
<li class="item">
<a href="#{item.href}" title="#{item.title}">#{item.text}</a>
<o:treeInsertChildren/>
</li>
</o:treeNodeItem>
</o:treeNode>
<o:treeNode>
<ul>
<o:treeNodeItem>
<li>
<a href="#{item.href}" title="#{item.title}">#{item.text}</a>
<o:treeInsertChildren/>
</li>
</o:treeNodeItem>
</ul>
</o:treeNode>
</o:tree>
</ul>
<h:outputScript id="mainnaviScript" target="body">
$(document).ready(function () {
$("#mainnavi").flexNav({'calcItemWidths': true});
});
</h:outputScript>
The OmniFaces' TreeModel with menu items is created programmatically. The Java code looks like
public TreeModel<NavigationItemDTO> getTreeModel() {
// get menu model from a remote service
NavigationContainerDTO rootContainer = remoteService.fetchMainNavigation(...);
TreeModel<NavigationItemDTO> treeModel = new ListTreeModel<>();
buildTreeModel(treeModel, rootContainer.getNavItem());
return treeModel;
}
private void buildTreeModel(TreeModel<NavigationItemDTO> treeModel, List<NavigationItemDTO> items) {
for (NavigationItemDTO item : items) {
buildTreeModel(treeModel.addChild(item), item.getNavItem());
}
}
And the end result (desktop variant):Note that submenus are clickable and can be expanded on mouseover.
You see, JSF is flexible and sometimes you don't need full-blown components. Have fun!
