Wednesday, June 01, 2011

JSP JSTL to render a tree menu

Sometimes we simply do not want to use Javascript to render an HTML tree.

Recursion is a common technique used to iterate through the Tree object and render it in HTML using ordered or unordered lists.

From JSP you could use a function but scriptlets are not desired as we know. A better practice is to use jsp:include to reload the same JSP page for the children of the current node.

Here is an example that builds a vertical menu keeping the needed separation of concerns in your architecture.

First there is a POJO encapsulating typical menu fields:
import java.util.ArrayList;
import java.util.List;

public class MenuItem {
    private String key;
    private String url;
    private List<MenuItem> menuItems;
    private boolean selected;
    
    public MenuItem(String key, String url, List<MenuItem> menuItems, boolean selected) {
        super();
        this.key = key;
        this.url = url;
        this.menuItems = menuItems;
        this.selected = selected;
    }
    
    public String getKey() {
        return key;
    }
    public void setKey(String key) {
        this.key = key;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public List<MenuItem> getMenuItems() {
        return menuItems;
    }
    public void setMenuItems(List<MenuItem> menuItems) {
        this.menuItems = menuItems;
    }  
    public void addMenuItem(MenuItem menuItem){
        if(menuItems == null){
            menuItems = new ArrayList<MenuItem>();
        }
        menuItems.add(menuItem);
    }
    public boolean isSelected() {
        return selected;
    }
    public void setSelected(boolean selected) {
        this.selected = selected;
    }
}

In your controller you should then fill the verticalMenu list:
List<MenuItem> verticalMenu = new ArrayList<MenuItem>();

The JSP calls itself:
<%@ include file="/WEB-INF/jsp/includes.jsp" %>
<ul>
<c:forEach var="menuItem" items="${verticalMenu}">
    <c:set var="verticalMenu" value="${menuItem.menuItems}" scope="request"/>
    <li>
        <c:choose>
            <c:when test="${menuItem.selected}">
                <a href="#" class="selected"><span><c:out value="${menuItem.key}" escapeXml="true"/></span></a>
            </c:when>
            <c:otherwise>
                <c:choose>
                    <c:when test="${ ! empty menuItem.url }">
                        <a href="<spring:url value="${menuItem.url}"/>"><span><c:out value="${menuItem.key}" escapeXml="true"/></span></a>
                    </c:when>
                    <c:otherwise>
                       <span><c:out value="${menuItem.key}" escapeXml="true"/></span>
                    </c:otherwise>
                </c:choose>
            </c:otherwise>
        </c:choose>
        <c:if test="${fn:length(menuItem.menuItems) > 0}">
            <jsp:include page="/WEB-INF/jsp/verticalMenu.jsp"/>
        </c:if>
    </li>
</c:forEach>
</ul>

And the CSS makes some adjustments:
#sidebar ul {
    margin: 0;
    padding: 0;
    list-style-type: none;
}

#sidebar ul ul {
    margin-left: 10px; 
}

#sidebar li {
    display: block;
    margin: 0;
    padding: 1px 0 0 2px; 
}

1 comment:

Genuine IPL Player said...

This is amazing. Thanks for sharing this example.

Followers