Spring Roo: Customizing Web MVC UI Forms

There are many features that I like from Spring Roo:

  • The shell is very effective for constructing projects -with a helpful tab completion-
  • Round-trip is essential to keep the code in sync
  • Code generation without “unnecessary abstractions” is refreshing -although version 1.1.2 might bring some of them back- just to mention a few.

At the top of the list, I am placing, what I consider the jewel-of-the-crown: The Tagx library for JSP(x) views. The article assumes some familiarity with Spring Roo. For more information on Spring Roo visit its website at http://www.springsource.org/roo.

Tagx Library in a Nutshell

The tagx library is installed by the Web MVC add-on. Spring Roo scaffolds controllers and views from entities classes. Views are created with components from the tagx library. Please read reference document at http://static.springsource.org/spring-roo/reference/html/base-web.html for a complete description.

Without further ado. Straight to business.

This article refers to forms implemented in World Almanac by jD website. World Almanac is a member of a group of web apps created to experiment with Spring Roo development. The link of the showcases website is at https://pragmatikroo.org/showcases. The purpose is to implement typical web development requirements beyond the CRUD scope.

This is the problem to work on: I need to customize a list.tagx component to render country flag images. Typically this component renders strings and numbers inside a grid. The form will be paginated for easy navigation, as shown in the image below. This functionality provides a World Almanac galleria view Additionally, I want to attach a click event for navigating to a country detail page.

The form provides two select controls for enabling counties filtering. The form is paginated and navigated the same way a typical list.jspx form is. Typically Roo select buttons are associated with an action button. In this case, I remove them and the filtering action is triggered by a choice select. I didn’t have to start from scratch. I just tweaked Roo generated source code. Roo is great!.

spring-roo-paginated-contries-form

Paginated Gallery Form

Below is the source code that renders the flag country gallery view. There is a lot of customization here. However you can still see the skeleton of the original scaffold form created by Roo.

Paginated Gallery JSPx code

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:jsp="http://java.sun.com/JSP/Page"
	xmlns:page="urn:jsptagdir:/WEB-INF/tags/form"
	xmlns:table="urn:jsptagdir:/WEB-INF/tags/form/fields" version="2.0"
	xmlns:field="urn:jsptagdir:/WEB-INF/tags/form/fields"
	xmlns:form="urn:jsptagdir:/WEB-INF/tags/form"
	xmlns:fn="http://java.sun.com/jsp/jstl/functions">
	
    
<script type="text/javascript">
    <![CDATA[
       
       function selectIt( aName ){
    	   var selectContinentPage="/worldAlmanac/countrys";
    	   document.body.innerHTML = '<form id="dynForm" action='+
    	                                 selectContinentPage+ 
    	                                 '  method="get">'+
    	                                 '<input type="hidden" name="find" value="gallery" />'+
    	                                 '<input type="hidden" name="\page" value="1" />'+
    	                                 '<input type="hidden" name="\size" value="50" />'+
    	                                 '<input type="hidden" name="'+aName.name+'" value="'+aName.value+'" />'+
    	                               '</form>';
           document.getElementById("dynForm").submit();
        }
        ]]>
    </script>

<jsp:output omit-xml-declaration="yes" /> 
 <div id="wrapperTop">
                  <input name="btnBack" title="Previous Page"
                    onMouseOver="window.status='Previous Page'; return true"
                    onMouseOut="window.status=''; return true"
                    onclick="location.href = '/worldAlmanac'"
                    type="button" value="Previous Page"/>
  </div>

	<page:list id="pl:org.josean.rooworld.domain.Country" 
	           items="${countrys}">
	<div>
		<div style="float: left; width: 60%;">
		
		      <field:select     disableFormBinding="true" 
				              id="f:org.josean.rooworld.domain.Country.continent"
				              items="${continent}" 
				              field="continent" 
				              path="/countrys"  
				              onchange="selectIt(this);"/>
		
		</div>
		<div>
			<field:select disableFormBinding="true"
				         id="f:org.josean.rooworld.domain.Country.region" 
				         items="${region}"
				         field="region" 
				         path="/countrys" 
				         onchange="selectIt(this);" />
		 </div>
	</div>

	<table:gallery data="${countrys}"
		id="l:org.josean.rooworld.domain.Country" path="/countrys"
        title="Click on image to select country info" page="${page}" size="${size}"
		typeIdFieldName="code" pageid="page" sizeid="size" delete="false"
		update="false" create="false" select="false" show="false"
		anchorURL="/countrys?find=gallery">
		<table:column id="c:org.josean.rooworld.domain.Country.code"
			type="img" property="code"  />

	</table:gallery>
		
</page:list>

<script type="text/javascript">
    <![CDATA[

             function applyStateControl(aControl, anItem){
         
                var option = aControl.getElementsByClassName("HTMLOptionElement");
                for( i=0; i<aControl.options.length;i++){
                     if(aControl.options.item(i).value==anItem)
                         break;
                    }
                aControl.options.item(i).defaultSelected=true;
             }
             var selectedItem= window.location.search;
             var cIndex = selectedItem.indexOf("continent");
             var rIndex = selectedItem.indexOf("region");
             if(cIndex>-1){
                 var continent= selectedItem.substring( cIndex+10, selectedItem.length);
                 var control = document.getElementById("_continent_id");
                 applyStateControl(control, continent);
                 
             }
             else if (rIndex>-1){
            	  var region= selectedItem.substring( rIndex+7, selectedItem.length);
                  var control = document.getElementById("_region_id");
                  applyStateControl(control, region);
             }
          
     ]]>
    </script>
</div>

Next, I show the country detail form navigated after clicking its flag. WorldAlmanac integrates easily Google Maps as well. It renders the country map using its latitude/longitude stored in the database. All elements together create a nice composition.!

spring-roo-detailed-country-form

Detailed Country Form

Detailed Country JSPx code

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div
 xmlns:field="urn:jsptagdir:/WEB-INF/tags/form/fields"
 xmlns:jsp="http://java.sun.com/JSP/Page"
 xmlns:page="urn:jsptagdir:/WEB-INF/tags/form"
 version="2.0"
 xmlns:spring="http://www.springframework.org/tags"
 xmlns:c="http://java.sun.com/jsp/jstl/core">
  <jsp:output omit-xml-declaration="yes" />

  <spring:url
   value="http://maps.google.com/maps/api/js?sensor=false"
   var="google_map_url" />

  <spring:url
   value="/static/js/googleMap.js"
   var="tmp_map_url" />

  <c:set
   var="map_url"
   value="uri:/${tmp_map_url}" />

<script
 type="text/javascript"
 src="${google_map_url}">
</script>

<script
 src="${tmp_map_url}"
 type="text/javascript">
</script>

  <page:show
   id="ps:org.josean.rooworld.domain.Country"
   object="${country}"
   z="3tU6/DN2doNWT+sTLgEAZR7U8hk=">
    <table
     width="100%;">
      <tr>
        <td
         width="50%">
          <field:display
           field="code"
           id="s:org.josean.rooworld.domain.Country.code"
           object="${country}"
           type="img"
           z="?"
           label="Flag" />
          <field:display
           field="name"
           id="s:org.josean.rooworld.domain.Country.name"
           object="${country}"
           z="FAyyuD7aX42uNNgL4e7tS//taRI=" />
          <field:display
           field="continent"
           id="s:org.josean.rooworld.domain.Country.continent"
           object="${country}"
           z="fSpvsKUctolI0akJTZzYHoOwueo=" />
          <field:display
           field="region"
           id="s:org.josean.rooworld.domain.Country.region"
           object="${country}"
           z="z7xfg8bZs8LFPftHWVjP45HdpGI=" />
          <field:display
           field="surfaceArea"
           id="s:org.josean.rooworld.domain.Country.surfaceArea"
           object="${country}"
           z="7C7ri5E3jCruBij5X5iEJqh4g1k=" />
          <field:display
           field="indepYear"
           id="s:org.josean.rooworld.domain.Country.indepYear"
           object="${country}"
           z="EZL1ubZ/J9SiMbCUfokjr1OoDKA=" />

          <field:display
           field="population"
           id="s:org.josean.rooworld.domain.Country.population"
           object="${country}"
           z="kqB3gvDWozV4lRD2m/S/6tpw2rE=" />
          <field:display
           field="lifeExpectancy"
           id="s:org.josean.rooworld.domain.Country.lifeExpectancy"
           object="${country}"
           z="6Sj9b9rtuCZd1V5oNnz7gmAaQkI=" />
          <field:display
           field="gnp"
           id="s:org.josean.rooworld.domain.Country.gnp"
           object="${country}"
           z="cQC9QwBNccJoYP9+xn4nAakFVVk=" />
          <field:display
           field="gnpold"
           id="s:org.josean.rooworld.domain.Country.gnpold"
           object="${country}"
           z="AKqXyf8qWGWBU0HwUSIvnnLzZeE=" />
          <field:display
           field="localName"
           id="s:org.josean.rooworld.domain.Country.localName"
           object="${country}"
           z="jYwa2XCaRe58W9ae45HLAaLlt7A=" />
          <field:display
           field="govermentForm"
           id="s:org.josean.rooworld.domain.Country.govermentForm"
           object="${country}"
           z="N9WckigFkEM6lBgGaHR3QG13Lq4=" />
          <field:display
           field="capital"
           id="s:org.josean.rooworld.domain.Country.capital"
           object="${country}"
           z="JRQSSvwHoOt1d2yff5JeK7NDn0k=" />
          <field:display
           field="code2"
           id="s:org.josean.rooworld.domain.Country.code2"
           object="${country}"
           z="gIR7Z72tUJCdEBJHExd4FLG1ze0=" />
          <field:display
           field="headofState"
           id="s:org.josean.rooworld.domain.Country.headofState"
           object="${country}"
           z="EmNqI9StH4djy7tAZsVKe3+VJ+A=" />
          <field:display
           field="latitude"
           id="s:org.josean.rooworld.domain.Country.latitude"
           object="${country}"
           z="?" />
          <field:display
           field="longitude"
           id="s:org.josean.rooworld.domain.Country.longitude"
           object="${country}"
           z="?" />
          <page:find
           reportParam="${country.code}"
           path="/pdf/countryReport"
           id="c:report"
           buttonLabel="Pdf Report" />
        </td>
        <td>
          <div
           id="map_canvas"
           style="width: 400px; height: 550px;">
            <button
             onclick="initialize();">Show Map</button>
          </div>
        </td>
      </tr>
    </table>
  </page:show>
</div>

That is it!

Conclusion

This article has shown a customization example using code originally started from the scaffold one created by the Spring Roo Web MVC add-on. I found the Jspx notation very expressive and intuitive. The tagx library is ample and easily to extend if needed. Jspx are XML documents, consequently they are validated against wellness. I have to say that this annoyed me in the beginning but after a short time, I got used to and started to like it. Not to mention the benefits of having forms error checking.

Spring Roo Web MVC views are simpler and less interactive their contra-parts based heavily in Javascript and Ajax. However, I believe they are very useful in a variety of Web Development projects and plenty of market for them. As the Reader can see, the look and feel of a Roo created app can totally be change to meet any UI/Web design requirement. This code example was create Spring Roo 1.1.0.M1.



19 Comments

  • Majkel 10 February, 2011, 6:11

    Cool, John, but the link to Pragmatik Roo Showcases shouldn’t be secure, Should it?
    This works:

    • Jose Delgado 5 March, 2011, 15:09

      Hello Macucu,

      If you are in the market for a position as a web security officer… let me know.
      Just focus in the article: Is there anything in the article that you like it or was useful to you?

      Thx
      jD

      • Majkel 13 January, 2012, 4:16

        For you who want to load the showcases of pragmatikroo, use http://pragmatikroo.org/showcases instead if the https link in the article. The pages doesn’t seem to work — they’re blank though there is code — at least in Safari 5.1 and Firefox 9 on Mac.

  • Majkel 10 February, 2011, 6:12

    Sorry, Jose. Link didn’t work.
    Trying again:
    http://pragmatikroo.org/showcases

    • Jose Delgado 5 March, 2011, 15:16

      Sorry Majkel,

      What you mean? It works!. From the showcases front-end click on World Almanac.showcase.
      That’s it. Next time I will be more explicit for telling the Reader where to click on.
      Thank you for bringing it to my attention.

      • Majkel 15 November, 2011, 3:42

        Late response: I guess the load of the almanac was slow back then. This one works now. The first grids and charts link also work but initially it takes up to 10 seconds to display properly , 5 of which occur after page seems fully loaded. Dual 2.0 G5 with 8GB RAM in Firefox 8.
        Thanks for your examples!

        • jD 22 November, 2011, 16:46

          Hello Majkel,

          If you can visit World Almanac by jD…
          I believe you will find it way-way faster. If interested I tell you what it did for it.

          Best results using Chrome. Firefox is ok too.

          Roogards
          jD

      • jD 15 November, 2011, 9:24

        Hello Majkel,

        You are very welcome…

        My response time -form here-is < than a second on the charts, filtering using different Continents. Which by the way the chart is generated every time.

        I notice that sometimes the rendering makes a short pause about in the middle page- rendering the flags. I haven't done any optimization at all. This is just a prototype for understanding how to move the CRUD code generated by Roo and find what it takes to create complete web applications.

        I am running multiple applications using the same web container which also impact performance.

        Roogards
        jD

  • Naveen 15 February, 2011, 11:10

    Viral,

    When you get time could you please post any examples on Spring Security with Database and Spring web-flow.

    Thanks,
    naveen

  • pramod pawar 23 November, 2011, 16:00

    How to retrive value from query string in spring roo………………..?

  • hello 12 December, 2011, 6:39

    Where did you get type=”img” attribute in display.tagx ? Did you modify roo’s display.tagx?

  • Marcos 12 January, 2012, 20:24

    Nice tutorial,
    but how i’m beginner in java
    i need de source code to understand
    more.

    it’s possible send me te source code?

    tanks,

    marcos

    sorry for bad english

    • jD 14 January, 2012, 17:44

      Hola Marcos,

      Spring Roo in Action de Manning is mucho mejor para principiantes…
      http://www.manning.com/rimple/

      Saludos y Buena Suerte
      jD

      • Soul Hunter 9 June, 2012, 2:36

        Hola

        Trate de replicar el ejemplo pero no detecta el tag Gallery.

        Como hago para obtenerlo??

        • Viral Patel 9 June, 2012, 3:13

          Translated to English:
          Hello
          Try to replicate the example but does not detect the tag Gallery.
          How do I get?

  • glauber dos santos 22 November, 2012, 23:40

    Hello, I need to understand better your source code. Can you send it to me? I am trying to load google maps in my springroo project but it just load first level of script(“http://maps.google.com/maps/api/js?sensor=false”) I noticed that the function getScript (getScript(“http://maps.gstatic.com/intl/pt_br/mapfiles/api-3/10/15a/main.js”);), for example, doesnt work, seems that there is some conflicts with webmvc and this function. I tried to download it and it works but when the day change they(google) change the validation key and the script doesnt work anymore

  • kiran 2 February, 2014, 14:34

    Can you please provide me the source code for customization

  • ganesh 28 April, 2014, 16:31

    Hi, I am getting the below error, what to do ?
    Please help me.

    No tag “gallery” defined in tag library associated with uri “urn:jsptagdir:/WEB-INF/tags/form/fields”

Leave a Reply

Your email address will not be published. Required fields are marked *

Note

To post source code in comment, use [code language] [/code] tag, for example:

  • [code java] Java source code here [/code]
  • [code html] HTML here [/code]

Current ye@r *