Chapter 29. Navigation Portlet Using the Public API

Portal API can be used to implement a Navigation Portlet.

29.1. Example Code

The only difference in the pom.xml standard portlet project code is the gatein-api dependency.

Example 29.1. pom.xml

<dependency>
  <groupId>org.gatein.api</groupId>
  <artifactId>gatein-api</artifactId>
</dependency>
This section cites code from Navigation API Portlet from the Quickstarts Collection ( https://docs.jboss.org/author/pages/viewpage.action?pageId=62587128) .

29.1.1. Required Java Classes

Two classes are required to implement the Navigation Portlet: NavigationPortlet and NavigationNodeBean .
The NavigationPortlet.doView(RenderRequest, RenderResponse) method is responsible for rendering the main persistent menu.

Example 29.2. The doView method

protected void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException {

PortalRequest portalRequest = PortalRequest.getInstance();

Navigation navigation = PortalRequest.getInstance().getNavigation();
        
// Diving two levels so the information about children count of children nodes is available
Node rootNode = navigation.getRootNode(Nodes.visitNodes(2));
navigationRootNodeBean = new NavigationNodeBean(rootNode);

List<NavigationNodeBean> rootNodeChildrenList = navigationRootNodeBean.getChildren();
 
/* Setting the 1st node to be active when accesing the root node "/" */
if (!rootNodeChildrenList.isEmpty() && portalRequest.getNodePath().equals(NodePath.root())) {
    navigationRootNodeBean.setFirstActive();
}
 
request.setAttribute("navigationRootNode", navigationRootNodeBean);
 
PortletRequestDispatcher prd = getPortletContext().getRequestDispatcher("/jsp/navigation.jsp");
prd.include(request, response);
     }
The NavigationPortlet.serveResource(ResourceRequest, ResourceResponse) method serves the AJAX requests to render sub-menus. For a given sub-menu URI, it returns a HTML fragment containing the sub-menu.

Example 29.3. The serveResource method

public void serveResource(ResourceRequest request, ResourceResponse response) throws PortletException, IOException {
 
Navigation navigation = PortalRequest.getInstance().getNavigation();

String chosenNodeURI = request.getParameter("uri");
 
Node chosenNode = navigation.getNode(NodePath.fromString(chosenNodeURI), Nodes.visitNodes(2));

request.setAttribute("parentNode", new NavigationNodeBean(chosenNode));
 
PortletRequestDispatcher prd = getPortletContext().getRequestDispatcher("/jsp/node.jsp");
prd.include(request, response);
     }
The NavigationNodeBean serves as a model and contains all the information being rendered on the page. In the NavigationPortlet.doView() method, the navigationRootNodeBean field is representing the root node of the navigation and contains main menu (top-menu) elements (Home and Sitemap by default) as children nodes.

29.1.2. Required JSP

JSP (JavaServer Pages) is used for rendering.
The navigation.jsp file is encapsulates the node.jsp output into an element with unique id identifier.

Example 29.4. Top Portlet JSP Source Code

<div id="id<portlet:namespace/>_gtnQuickstartNavigationPortlet" class="gtnQuickstartNavigationPortlet ">    
    <%-- Render the main menu, if nodes are available --%>
    <c:if test="${fn:length(navigationRootNode.children) > 0}">
        <c:set var="parentNode" value="${navigationRootNode}" scope="request" />
        <c:set var="menuType" value="topmenu" scope="request" />        
        <jsp:include page="node.jsp" />
    </c:if>
</div>
The node.jsp file is responsible for rendering the menus.

Note

The HTML comment tag does not disable the JSP code. It is used to remove the whitespace for more precise CSS styling.

Example 29.5. Menu JSP Source Code

<ol class="${menuType} collapsibleContent"><!--
     <c:forEach var="child" items="${parentNode.children}">
<c:if test="${!child.system}">
     --><li class="menuitem <c:if test='${child.active}'>active</c:if> <c:if test='${(child.parent) &amp;&amp; (child.page)}'> multilevel</c:if>">        
     <portlet:resourceURL var="ajaxResourceUrl">
<portlet:param name="uri" value="${child.path}" />
     </portlet:resourceURL>
     <c:choose>
<%-- Node is a clickable page and contains children nodes --%>
<c:when test="${child.page &amp;&amp; child.parent}">
    <a href="${child.URI}"><span>${child.name}</span></a><!--
    --><a href="#${ajaxResourceUrl}" class="caret menuhandler"><i>${resourceBundle.getString("label.children")}</i></a>
</c:when>
<%-- Node is a clickable page but doesn't contain any children nodes --%>
<c:when test="${child.page &amp;&amp; !child.parent}">
    <a href="${child.URI}"><span>${child.name}</span></a>
</c:when>
<%-- Node is not a clickable page but contains children nodes, it's a "category" node --%>
<c:when test="${!child.page &amp;&amp; child.parent}">
    <a href="#${ajaxResourceUrl}" class="menuhandler"><span>${child.name}</span><i class="caret">${resourceBundle.getString("label.children")}</i></a>
</c:when>
<%-- Do nothing with non-clickable "category" nodes without children nodes --%>
<c:otherwise>
    <span>${child.name}</span>
</c:otherwise>
    </c:choose>
</li>
<!--
</c:if>
</c:forEach>
-->
</ol>

29.1.3. Required JavaScript

The JavaScript part was implemented as a JQuery plug-in. It has several roles:
  • AJAX support
  • Basic on-click drop-down support
  • Dynamically respond to window changes, position menus within the browser window
The asynchronous requests use the JQuery ajax() method:

Example 29.6. The JQuery plugin for drop-down menu


$.ajax({
    type: "POST",
    url: $(this).attr('href').substring(1),
    cache: false,
    dataType: "text",
    success: function(data)
    {
        menuItem.append(data);
        openSubmenu(menuItem);
    },
    error: function(XMLHttpRequest, textStatus, errorThrown)
    {
        console && console.log("Ajax error");
    }
});
For more details about the implementation, see the comments in the dropdown.jquery.js file.

29.1.4. Further Steps

Read and complete the following sections to test the functionality: