Monday, December 8, 2014

Building dynamic responsive multi-level menus with plain HTML and OmniFaces

Recently, I had to create a responsive multi-level menu with JSF 2.2. Requirements: the menu should
  • 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
PrimeFaces' menus were not a choice. They can indeed be created programmatically by model, but
  • they are not really responsive
  • submenu items only collapse / expand the submenus and can not contain navigation links
  • ...
Well, why not to pick any jQuery based plugin for responsive multi-level menus? There are a lot of plugins. See Useful List of Responsive Navigation and Menu Patterns. I choosen FlexNav.

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!

7 comments:

  1. Very helpful, I was looking for something like this
    Thanks Oleg,
    (Please post something like this to create autocomplete control with more features than Primefaces control)

    ReplyDelete
  2. Great Post, I love to read articles that are informative and actually have good content. Thank you for sharing your experiences and I look forward to reading more.
    Ryan Levin | Ryan Levin

    ReplyDelete
  3. This is really very informative blog i always visit this blog keep blogging great!!!!!
    Software Development Company in Lucknow

    ReplyDelete
  4. As a web development and design service provider Webzin InfoTech provides optimum solutions to customer by helping them to achive their goals and make available their services and products in the online market.

    ReplyDelete
  5. it's helpful coding for responsive navigation menu. Thanks for sharing informative post.
    Web Design Company India | Web Development Company India

    ReplyDelete

Note: Only a member of this blog may post a comment.