views:

216

answers:

2

I'm starting out a Spring MVC 3.0 application and getting controllers and views working nicely, the one thing I now need to do is get navigation working. So for each view I have, it needs to know what item in the "menu" part of the view to highlight and what menu options are available to that view.

One thing I thought of was to get the controller to tell the view where it is, but that's binding view logic into the controller. My views menu is in its own include which I include within each view.

What are your approaches? I'm thinking of using tiles, to this point I've only been using JSP and JSTL, maybe this will help?

A: 

Looks like this question is seeing tumbleweeds. I've given the problem a lot of though, haven't found any viable solutions so figured I'd write an implementation of an idea that I have, I'm posting this for your comments, tell me if you think it's a s**t idea, if you think it's worthy, I'll post the resultant code.

Firstly, I'm planning to solve this with a simple request-less servlet that loads XML menu file resources (initially just one, but support for more later). A menu include can then useBean the menus loaded and renders in any way the user wishes, default goal is a simple unordered list, which is fairly common.

So first, my menu might look like the following, it's got provision for security roles, but that might be something as an "added extra" by creating two versions SimpleMenuServlet and SpringSecurityMenuServlet or something similar.

<menus>
   <menu id='home' path='/home'>
      <paths>
         <match pattern='/home'/>
      </paths>
   </menu>
   <menu id='administration' path='/admin'>
      <allowed-roles>
         <role name='admin'/>
      </allowed-roles>
      <paths>
         <match pattern='/admin/**'/>
      </paths>
   </menu>
   <menu id='reports' path='/reports'>
      <allowed-roles>
         <role name='user'/>
         <role name='admin'/>
      </allowed-roles>
      <paths>
         <match pattern='/reports/**'/>
         <match pattern='/item/*/reports/**'/>
      </paths>
   <menu>
</menus>

NOTE: there is no "Text" attibute on menus as this is intended to be returned from resource bundles.

Rendering can now be done with a useBean from the servlet, or via a custom tag library, which I don't think is really necessary, but who knows.

<ul id="main-menu">
<c:forEach var="item" items="${menu-items}">
   <c:choose>
      <c:when test="${item.selected && !found}">
         <c:set var="menuClass" value="selected"/>
         <c:set var="found" value="selected"/>
      </c:when>
      <c:otherwise>
         <c:set var="menuClass" value=""/>
      </c:otherwise>
   </c:choose>
   <li id="${item.id}" class="${menuClass}">
      <!-- Using resource bundle to get text, this could be the
           servlet/menu item that's responsible to get this instead -->
      <a href="${item.path}"><fmt:message key="menu-${item.id}" /></a>
   </li>
</c:forEach>
</ul>

So now my reasoning is that I could JAR the servlet and menu beans up, control it through web.xml with respect to loading menu files and startup etc, then render by any view.

As you can see the core of the menu works by path patterns determining which area you're in, it doesn't handle page linking like faces does, but that's not what I'm intending, I'm looking for a static, configurable, maintainable menu, that neither my views nor my controllers have to have a great deal of knowledge of.

Brett Ryan
+1  A: 

Hi, I recommend you to use tiles as a view manager, and a viewNameTranslator like this:

<bean id="viewNameTranslator" class="org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator">
    <property name="separator" value="." />
</bean>

So this way you can avoid specifing view names on controller class returning void.

later on tile-def.xml you define a view somethin like this:

<definition name="newView" extends="baseView">
    <put name="menu" value="/pages/menu/menu.jsp?highlightedView=newView" />
    <put name="body" value="/pages/newStuff/content.jsp" />
</definition>   

This way menu.jsp receives an aditional parameter highlightedView with the info you need.-

Hope it helps.

ejmarino
That's a great solution ejmarino, thank you. I am already using tiles so this was pretty simple to do. It does mean that in my menu.jsp I have to put a lot of `<c:test/>` blocks to determine if the class should be applied to that `li`, though this could be simplified with a small piece of JavaScript. Thanks again, great answer.
Brett Ryan