最近,我不得不使用JSF 2.2创建一个响应式多级菜单。 要求:菜单应:
- 从后端使用动态结构创建
- 反应灵敏,例如对桌面和移动设备友好
- 有带有导航链接的子菜单项
- 支持触摸事件
- 支持键盘辅助功能
PrimeFaces的菜单不是一个选择。 实际上,可以通过模型以编程方式创建它们,但是:
- 他们没有真正回应
- 子菜单项只能折叠/展开子菜单,不能包含导航链接
- …
好吧,为什么不为响应式多级菜单选择任何基于jQuery的插件呢? 有很多插件。 请参阅响应式导航和菜单模式的有用列表 。 我选择了FlexNav 。
但是如何输出动态菜单结构呢? ui:repeat在这里不是一个选择,因为该结构(嵌套子菜单等)不是先验的。 幸运的是,OmniFaces具有o:tree ,通过声明标记中的JSF组件或HTML元素,可以完全控制树层次结构的标记。 o:tree本身不会呈现任何HTML标记。 正是我需要的!
我最后得到了这个XHTML片段,其中混合了o:treeNode,o:treeNodeItem,o:treeInsertChildren和由提到的FlexNav菜单定义HTML元素:
<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>
带菜单项的OmniFaces的TreeModel是通过编程创建的。 Java代码如下所示:
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());
}
}
最终结果(桌面版本):
请注意,子菜单是可单击的,并且可以在鼠标悬停时展开。
您会发现,JSF是灵活的,有时您不需要成熟的组件。 玩得开心!