Struts has support for indexed properties in its form beans. In fact, it has support for both simple and complex indexed properties. Ironically I have been able to find lots of documentation online to explain the more complex situation but none for the simpler one. I have been able to find documentation on using arrays of beans is form beans but not of arrays of simple literals like strings and integers. And I’ve done a lot of googling on the matter. Having the ability to have an array of strings in your form bean is a very handy feature. This is a very handy thing to be able to do and you’d be right to assume that it should be simple, and it is simple, it’s just not documented anywhere I could find (and I spend a lot of time looking). So, to help others who might be stuck with the same problem here is a worked example that should make it all clear.

Defining the Problem

This code example is taken straight form the VTIE Collaborative Writing Environment for use in education which I’m Lead Developer on for the EVE Research Group in NUI Maynooth. This whole project will be GPLed soon (when the code base is stable), so I don’t mind showing un-edited code snippets.

We are going to be looking at a single interface within the environment that allows a teacher to add a new group (or class) of students to the system. This, in the most complex situation, is a three step process, two for gathering data and one for actually doing the work and adding the group to the system. The first step allows the teacher to specify information about the group as a whole and the second step allows the teacher to enter the names of the students. This second step is optional. Teachers can choose between an anonymous group (for minors) and a named group (for adults and older children). If they choose an anonymous group the second step is skipped. Lets have a look at what the forms for the two information gathering steps look like with some screen shots:

Figure 1 - Create Group Form Step 1

Figure 2 - Create Group Form Step 2

The thing to note is that on the second page the number of text areas depends on the number requested on the first page. This makes it impossible to represent these text areas in the form object as regular string elements because you don’t know how many you’ll need. There are a couple of ways of fudging it but trust me, they are all exceptionally bad software engineering! The correct solution is to store the student names as an array, generated after the submission of the first page before the display of the second page and to have one text box mapped to each array element.

Creating the Form Object

In the VTIE project we use dynamic form objects generated from XML in struts-config.xml for our forms because there is no point in wasting time writing a class for each form in your webapp when you don’t have to. To do this we use the class org.apache.struts.validator.DynaValidatorForm for our forms.

Although the data input happens over multiple pages we only use one form object (which will reside in session) because we want all the information together when we finally submit it to the action that will create the group. Below is the XML from struts-config.xml for the form object used for this operation:

  1. <!-- Group creation form -->
  2. <form-bean name="createStudentGroupForm" type="org.apache.struts.validator.DynaValidatorForm">
  3.  <form-property name="groupName" type="java.lang.String" />
  4.  <form-property name="noStudents" type="java.lang.Integer" initial="10" />
  5.  <form-property name="groupType" type="java.lang.String" />
  6.  <form-property name="wikiText" type="java.lang.String" />
  7.  <form-property name="studentNames" type="java.lang.String[]" />
  8.  <form-property name="page" type="java.lang.Integer" />
  9. </form-bean>

I just want to draw your attention to a two things in the above form definition. Firstly, we give the type of the attribute that will hold the student names as java.lang.String[] (an array of strings) but, and this is important, we do not give the array a size. By doing so we allow the array to be any size we want, but we have to initialize it in an action before we try to use it in a JSP page. We will initialize it in the action that the first page is submitted to. The second thing I want to draw your attention to is the property page. This property is used by the Struts Validator to figure out when to validate what form elements. You need this element on all forms that use the validator and collect their data over multiple pages. It is vital that this property have the type java.lang.Integer.

Setting up the Actions

There are three actions involved in this operation. The first one is the action that the first page of the form submits the first lot of data to (including the number of students the group will have). This action is mapped to /mentor/createStudentGroupStep1 and is responsible for the following:

  • Initializing the array of student names in the form object.
  • Deciding where to forward to next, straight to the action to actually add the group if the teacher asked for a anonymous group, or to the jsp for entering names if a named group was requested.

The second Action is called by the second page of the form when it has gathered the student names and is used to create a named group. Finally, the third action is called straight from the first action to create an anonymous group. Both of these actions are actually implemented by the same class because it turns out that there is no real difference between creating a named group and an anonymous group.

Below are the action mapping for these three actions in struts-config.xml:

  1. <action path="/mentor/createStudentGroupStep1" type="vtie.portal.mentor.CreateStudentGroupPrepareAction" name="createStudentGroupForm" scope="session" validate="true" input="/home/mentor/addStudentGroupForm.jsp">
  2.  <forward name="createAnonymous" path="/do/mentor/createAnonymousStudentGroup" />
  3.  <forward name="getStudentNames" path="/home/mentor/getStudentNamesForm.jsp" />
  4.  <forward name="fail" path="/home/mentor/addStudentGroupForm.jsp" />
  5. </action>
  6. <action path="/mentor/createAnonymousStudentGroup" type="vtie.portal.mentor.CreateStudentGroupAction" name="createStudentGroupForm" scope="session" validate="false">
  7.  <forward name="success" path="/do/mentor/showStudentGroup" />
  8.  <forward name="fail" path="/home/mentor/addStudentGroupForm.jsp" />
  9. </action>
  10. <action path="/mentor/createNamedStudentGroup" type="vtie.portal.mentor.CreateStudentGroupAction" name="createStudentGroupForm" scope="session" validate="true" input="/home/mentor/getStudentNamesForm.jsp">
  11.  <forward name="success" path="/do/mentor/showStudentGroup" />
  12.  <forward name="fail" path="/home/mentor/getStudentNamesForm.jsp" />
  13. </action>

The things to note here are that we use the same form in all three actions, that we always call the validator and that we always set the form to session scope. Failing to do these three things will cause the whole lot to stop working.

Implementing The Actions

Now lets have a look inside our two action classes, starting with the one for the action /mentor/createStudentGroupStep1 (vtie.portal.mentor.CreateStudentGroupPrepareAction). Note my actions only implement the function ActionForward so that’s all I’m showing here:

  1. public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
  2.   Logger log = new Logger("Prepare Create Student Group Action", false);
  3.   HttpSession session = request.getSession(true);
  4.  
  5.   // ensure it's a mentor doing this
  6.   SessionHelper.forceMentor(session);
  7.  
  8.   //get the dynamic form
  9.   DynaValidatorForm theForm = (DynaValidatorForm)form;
  10.  
  11.   //get the details from the form
  12.   String groupType = (String)theForm.get("groupType");
  13.   int numStudents = ((Integer)theForm.get("noStudents")).intValue();
  14.  
  15.   //prepare the list of default student names
  16.   String students[] = new String[numStudents];
  17.   for(int i = 0; i < numStudents; i++) {
  18.     students[i] = LoginService.generateStudentUserName(i);
  19.   }
  20.  
  21.   //insert into the form
  22.   theForm.set("studentNames", students);
  23.  
  24.   if(groupType.equals("ADULTS")) {
  25.     //named group
  26.     return mapping.findForward("getStudentNames");
  27.   } else {
  28.     //anonymous group
  29.     return mapping.findForward("createAnonymous");
  30.   }
  31. }

The important thing to note is that we are initializing the array of student names here and inserting it into the form.

Finally lets look at the ActionForward function on the class vtie.portal.mentor.CreateStudentGroupAction which implements both the /mentor/createAnonymousStudentGroup and /mentor/createNamedStudentGroup actions:

  1. public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
  2.   Logger log = new Logger("Create Student Group Action", false);
  3.   LoginService service = new LoginService();
  4.   HttpSession session = request.getSession(true);
  5.   MessageResources messageResources = getResources(request);
  6.  
  7.   // ensure it's a mentor doing this
  8.   SessionHelper.forceMentor(session);
  9.  
  10.   //get the dynamic form
  11.   DynaValidatorForm theForm = (DynaValidatorForm)form;
  12.  
  13.   // instance variables
  14.   String groupId = "";
  15.   String mentorId = SessionHelper.getMentorId(session);
  16.  
  17.   //get the details from the form
  18.   String groupName = (String)theForm.get("groupName");
  19.   int noStudents = ((Integer)theForm.get("noStudents")).intValue();
  20.   String wikiText = (String)theForm.get("wikiText");
  21.   if(wikiText == null) {
  22.     wikiText = "";
  23.   }
  24.   PaperContent wikiContent = PaperContent.fromHtml(wikiText);
  25.   String studentNames[] = (String[])theForm.get("studentNames");
  26.   String groupType = (String)theForm.get("groupType");
  27.  
  28.   //try to create the student group
  29.   try {
  30.     boolean anon = true;
  31.     if(groupType.equals("ADULTS")) {
  32.       anon = false;
  33.     }
  34.     groupId = service.addNewStudentGroup(mentorId, groupName, wikiContent, studentNames, anon);
  35.   } catch(Exception e) {
  36.     log.iLogError("Failed to  create a new Student group of the name " + groupName + ", beloning to the mentor id " + mentorId, e);
  37.     e.printStackTrace();
  38.     RequestHelper.setErrorMessage(request, messageResources.getMessage("mentor.createStudentGroup.fail"));
  39.     return (mapping.findForward("fail"));
  40.   }
  41.  
  42.   try {
  43.     request.setAttribute("groupId", groupId);
  44.     RequestHelper.setMessage(request, messageResources.getMessage("mentor.createStudentGroup.success", groupName));
  45.     log.iLogInfo(mentorId + " created a new student group named " + groupName + " with " + noStudents + " students in it.");
  46.     return (mapping.findForward("success"));
  47.   } catch(Exception e) {
  48.     e.printStackTrace();
  49.     log.iLogError("Failed to create the student group bean and add it to the request");
  50.     RequestHelper.setErrorMessage(request, messageResources.getMessage("mentor.createStudentGroup.fail"));
  51.     return (mapping.findForward("fail"));
  52.   }
  53. }

There’s nothing really special to note about this action, it just does some security checks, grabs the details (including our array of names) from the form and then sends them off to a function in the service class which sticks them in the DB. I really only included the above code for completeness.

Setting up the JSPs for the View

There are two JSPs needed for the view, one to display the first page of the form and one for the second. I’m not going to bore you with the entire JSP for these pages but rather just show you the JSP for the form in each. Those among you who are observant will notice that I have implented a custom tag library for the VTIE project but don’t worry about that, those tags just deal with looking after the layout and aesthetics of the page and don’t do anything that impacts the functionality of the form in any way.

Lets start by having a look at the JSP for rendering the form on the first page of the form (/home/mentor/addStudentGroupForm.jsp):

  1. <vtie:ifLevel level="MENTOR">
  2.  <vtie:formContainer key="mentor.createStudentGroup.pageTile">
  3.   <vtie:systemMessage />
  4.   <p id="html_errors"><html:errors /></p>
  5.  
  6.   <script type="text/javascript">
  7.     function encodeWikiText(){
  8.       tinyMCE.triggerSave();
  9.       this.theForm = document.getElementById('createStudentGroupForm');
  10.       this.theForm.wikiText.value = sanitiseHTML(document.getElementById("wiki_text").value);
  11.       return true;
  12.     }
  13.   </script>
  14.   <html:form action="/mentor/createStudentGroupStep1" styleClass="vtie_form" onsubmit="encodeWikiText()">
  15.   <h2><bean:message key="group.details.label" /></h2>
  16.   <ul class="form_element_list">
  17.    <li><label for="groupName"><bean:message key="mentor.createStudentGroup.groupName.label" /></label> <html:text property="groupName" /></li>
  18.    <li><label for="noStudents"><bean:message key="mentor.createStudentGroup.noStudents.label" /></label> <html:text property="noStudents" /></li>
  19.    <li><label for="groupType"><bean:message key="mentor.createStudentGroup.groupType.label" /></label>
  20.     <html:select property="groupType">
  21.      <html:option value="MINORS"><bean:message key="mentor.createStudentGroup.groupType.minors" /></html:option>
  22.      <html:option value="ADULTS"><bean:message key="mentor.createStudentGroup.groupType.adults" /></html:option>
  23.     </html:select>
  24.    </li>
  25.   </ul>
  26.   <h2><bean:message key="group.description.label" /></h2>
  27.   <html:hidden property="wikiText" />
  28.   <html:hidden property="page" value="1"/>
  29.   <p>
  30.   <textarea id="wiki_text" cols="40" rows="10"></textarea>
  31.   </p>
  32.   <p class="button_bar"><html:submit styleClass="submit" /><vtie:cancel /></p>
  33.   </html:form>
  34.  </vtie:formContainer>
  35. </vtie:ifLevel>

This is just a perfectly normal Struts form. There is some JS funniness to make the nice WYSIWYG HTML editor fit in correctly but nothing more than that. The thing to note here is the hidden form element called page. Leaving this out would break the validator.

Now lets look at the more interesting form, the second one that lets us enter our student names in /home/mentor/getStudentNamesForm.jsp:

  1. <vtie:ifLevel level="MENTOR">
  2.  <vtie:formContainer key="mentor.createStudentGroup.getNames.pageTile">
  3.   <vtie:systemMessage />
  4.   <p id="html_errors"><html:errors /></p>
  5.  
  6.   <html:form action="/mentor/createNamedStudentGroup" styleClass="vtie_form">
  7.  
  8.   <ul class="form_element_list">
  9.    <jsp:useBean id="createStudentGroupForm" scope="session" type="org.apache.struts.validator.DynaValidatorForm" />
  10.    <logic:iterate id="student" name="createStudentGroupForm" property="studentNames" indexId="i">
  11.     <li><label for="student<bean:write name="i" />"><bean:message key="mentor.createStudentGroup.getNames.studentName.label" /></label> <html:text property="studentNames[${i}]" styleId="student${i}" /></li>
  12.    </logic:iterate>
  13.   </ul>
  14.   <html:hidden property="page" value="2" />
  15.   <p class="button_bar"><input type="button" value="<bean:message key="generic.back" />" onclick="location.href=CONTEXT_PATH+'/home/mentor/addStudentGroupForm.jsp'" /><html:submit styleClass="submit" /><vtie:cancel /></p>
  16.   </html:form>
  17.  </vtie:formContainer>
  18. </vtie:ifLevel>

The important thing to note here is how we render the list of textboxes for the names by using the logic:iterate tag from the struts taglibs. Again note the hidden page element in the form and how this time it has a value of 2.

Validating The Form

One of the nicest things about Struts is the Validator that leaves your action code free to just get on with what it has to do safe in the knowledge that the data it gets is valid. This validation is controlled by (you guessed) an XML file (validator.xml is the usual but it can be changed). All our work to get an array into a form would be pointless if the validator could not handle arrays. It can but no one bothered to document that fact (except for arrays of objects). It took me ages to get this figured out mainly because the syntax is rather odd. I did eventually track down the answer in a PowerPoint presentation I found on Google but it took some time. Anyhow, below is the validation form validation.xml for this form.

  1. <form name="createStudentGroupForm">
  2.  <field property="groupName" page="1" depends="required, mask">
  3.   <var><var-name>mask</var-name><var-value>${nameRE}</var-value></var>
  4.   <arg0 key="mentor.createStudentGroup.groupName.label"/>
  5.  </field>
  6.  <field property="noStudents" page="1" depends="required,integer,intRange">
  7.   <arg position="0" key="mentor.createStudentGroup.noStudents.label"/>
  8.   <arg position="1" name="intRange" key="${var:min}" resource="false"/>
  9.   <arg position="2" name="intRange" key="${var:max}" resource="false"/>
  10.   <var><var-name>min</var-name><var-value>1</var-value></var>
  11.   <var><var-name>max</var-name><var-value>99</var-value></var>
  12.  </field>
  13.  <field property="studentNames" indexedListProperty="studentNames" page="2" depends="required, mask, maxlength">
  14.   <arg0 key="mentor.createStudentGroup.studentNames.label"/>
  15.   <arg position="1" name="maxlength" key="${var:maxlength}" resource="false"/>
  16.   <var><var-name>maxlength</var-name><var-value>25</var-value></var>
  17.   <var><var-name>mask</var-name><var-value>${nameRE}</var-value></var>
  18.  </field>
  19. </form>

The thing to note is the strange use of the indexedListProperty in the field element for our array of student names. You have to tell the validator that you are validating the field and that you are validating the same field as an indexed field. Once you do that the validation is applied to all elements in the array and if any one element fails the validation for the form fails and you get sent back to the JSP to get shown the error and asked to try again. The other thing to note is the use of the page attribute of the field elements to tell Struts when to validate what. At any time it validates all fields with a page value less than or equal to the page element it recieved from the form.

Message Resources

In a Struts app all your language specific text should be defined in the ApplicationResources.properties file. This makes internationalization trivial, you just get someone to translate this file for you, name it with the ISO code for the language and add it to your app. In the case of VTIE we have English as our default language but we also have an Irish version and a French version defined in ApplicationResources_ga.properties and ApplicationResources_fr.properties respectively.

For the sake of completeness here are the relevant lines from the default ApplicationResources.properties file:

  1. breadcrumbs.mentorHome=Mentor Home Page
  2. breadcrumbs.mentor.createGroup=Create Student Group
  3.  
  4. mentor.createStudentGroup.pageTile=Creat Student Group
  5. mentor.createStudentGroup.groupName.label=Group Name
  6. mentor.createStudentGroup.noStudents.label=Number of Students
  7. mentor.createStudentGroup.groupType.label=Group Type
  8. mentor.createStudentGroup.groupType.minors=Minors
  9. mentor.createStudentGroup.groupType.adults=Adults
  10. mentor.createStudentGroup.getNames.pageTile=Enter Student Names
  11. mentor.createStudentGroup.getNames.studentName.label=Student Name
  12. mentor.createStudentGroup.studentNames.label=Student Names
  13. mentor.createStudentGroup.success=Created the group '{0}'.
  14. mentor.createStudentGroup.fail=An error occoured while creating the group.
  15.  
  16. group.details.label=Group Details
  17. group.description.label=Group Description
  18.  
  19. generic.ok=OK

Final Thoughts and Some Useful Resources

Something that annoys me about a lot of open source projects is the lack of detailed documentation and Struts suffers from this too. There is a lot of good information on the basics of Struts on the Struts web page so the beginner is very well catered for. However, the more advanced user will often find the documentation is not detailed enough. There is JavaDoc documentation available for all Struts classes but this is useless for the elements that you don't interact with through Java directly. The best examples of this are the Validator, the struts-config.xml file, and the logging features in Struts. The basics of all of these features are well covered in the User's Guide but when you need the full specs this documentation is not sufficient. The Struts taglibs are the exception in this case, there is very good detailed documentation on these. Hopefully the Struts Wiki will begin to address this problem but for the moment developing in Struts can get very frustrating when you start doing non-standard things. The mailing lists are good but they don't have all the answers and I would much prefer to have a manual to go read rather than have to annoy people on a mailing list for things that you should just be able to RTFM.

Anyhow, if you are developing in Struts these are the resources I have found invaluable:

Technorati Tags: , , ,

Comments

24 Responses to “Simple Indexed Properties in Struts Forms – The Missing Documentation”

  1. Ashok redddy on June 22nd, 2007 8:34 pm

    I learned a lot from this code. it is very helpful to a every body.thanks a lot.

  2. Ronan Dowd on June 25th, 2007 5:30 pm

    hi, great article, really helped me. quick question though. On the “Enter Student Names” screen – if the user enters incorrect data for rows 1, 3 and 5 (for example) upon clicking the “Submit” button – when he gets taken back to the same page (after validation has run) will it be clear *which* rows were erroneous or will the error messages appear just above the input field for each offending row.?

    It would be nice to have the error messages all at the top with a “Row – error msg” type message so the user could tell which row the error was on.
    Thanks, Ronan.

  3. Bart B on June 25th, 2007 7:20 pm

    Hi Ronan, glad my article was of some help to you. My code does indeed not identify which names are in error when validation fails. Only one error message is returned by the validator so I just print it.

    I haven’t tried this but you could try setting the errorStyleClass or errorStyle attributes of the html:text tags and see if that can help you make it clearer what exact fields are in error. Like I say I haven’t tried this so it may not work, either way it would be great if you could report back here on how you get on.

  4. Rahul on July 18th, 2007 1:05 pm

    Hi,

    I tried the same on my application.
    But I am getting this exception on submitting the form

    javax.servlet.ServletException: BeanUtils.populate
    org.apache.struts.util.RequestUtils.populate(RequestUtils.java:497)
    org.apache.struts.action.RequestProcessor.processPopulate(RequestProcessor.java:798)
    org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:205)
    org.apache.struts.action.ActionServlet.process(ActionServlet.java:1164)
    org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:415)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:709)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:802)

    root cause

    java.lang.ArrayIndexOutOfBoundsException
    java.lang.reflect.Array.set(Native Method)
    org.apache.struts.action.DynaActionForm.set(DynaActionForm.java:458)
    org.apache.commons.beanutils.PropertyUtils.setIndexedProperty(PropertyUtils.java:1414)
    org.apache.commons.beanutils.BeanUtils.setProperty(BeanUtils.java:1013)
    org.apache.commons.beanutils.BeanUtils.populate(BeanUtils.java:808)
    org.apache.struts.util.RequestUtils.populate(RequestUtils.java:495)
    org.apache.struts.action.RequestProcessor.processPopulate(RequestProcessor.java:798)
    org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:205)
    org.apache.struts.action.ActionServlet.process(ActionServlet.java:1164)
    org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:415)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:709)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:802)

    I also defined the form property as String[] without size.

  5. Bart B on July 18th, 2007 1:16 pm

    Hi Rahul,

    It is important that the array be defined without a size in the XML but it is equally vital that it be initialized with a size before you try to access it in any way. If you look at my above example you’ll see that the action which the first page of the form submits to initializes the array with the appropriate size before trying to render the form elements.

    Hope that helps,

    Bart.

  6. Sunny on July 23rd, 2007 11:44 am

    Hi,

    I am trying the similar thing with Indexed Property
    but getting But I am getting this exception on submitting the form…

    exception

    javax.servlet.ServletException: BeanUtils.populate

    root cause

    java.lang.reflect.InvocationTargetException
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    java.lang.reflect.Method.invoke(Method.java:585)
    org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod(PropertyUtilsBean.java:1773)
    org.apache.commons.beanutils.PropertyUtilsBean.setIndexedProperty(PropertyUtilsBean.java:1391)
    org.apache.commons.beanutils.BeanUtilsBean.setProperty(BeanUtilsBean.java:1016)
    org.apache.commons.beanutils.BeanUtilsBean.populate(BeanUtilsBean.java:811)
    org.apache.commons.beanutils.BeanUtils.populate(BeanUtils.java:298)

  7. Bart B on July 23rd, 2007 11:51 am

    Hi Sunny,

    Nothing obvious comes to mind from reading that exception but there really is very little to go on there.

  8. Sunny on July 23rd, 2007 12:16 pm

    Hi,

    I write i very simple code to populate a String Array :-

    This is my jsp
    ==============

    ” styleId=”" style=”width: 40px” size=”5″ maxlength=”6″ styleClass=”TextFild” />

    And in my Form i declare one property :-
    ————————————–
    private String[] sPercentages;

    and its getter and setter:-
    ————————-
    public String[] getSPercentages() {
    return sPercentages;
    }
    public void setSPercentages(String[] sPercentages) {
    this.sPercentages = sPercentages;
    }
    and i also initialize the array in reset method.
    I am confuse alot like where i am doing mistake.
    on submit i am getting errors.

  9. Sunny on July 23rd, 2007 12:17 pm

    This is my jsp :-
    ==============

    ” styleId=”" style=”width: 40px” size=”5″ maxlength=”6″ styleClass=”TextFild” />

  10. Sunny on July 23rd, 2007 12:31 pm

    Hi,

    I use for loop and HTML:text property=”"
    and String second = “sPercentages["+ i +"]“;

  11. Bart B on July 23rd, 2007 12:37 pm

    Hi Sunny,

    This technique uses two steps, first ask the user how many of something they need, then let them enter that many things. Hence, there are two submits. Which one is your code failing on?

    I’ve never used this technique with a form not created using a dynamic form in struts-config.xml. The exception you have above is one thrown by constructors. I’d suggest having a look at the default constructor for your form.

    Also, at what point do you initialize the array in your form so it has a size? You have to do that when you submit the first step and before rendering the second step.

  12. mr_squirtle on August 1st, 2007 11:27 am

    Hi Bart,

    Is there any zip file for this project. I’m quite new to index property of struts and just googled it and found your web site. I find it informative and interesting but I cant seem to run it because its looking for other files and classes not shown here.

    Is there a link where I can download the code? Thanks..

  13. Bart B on August 1st, 2007 11:31 am

    Hi Mr_Squirtle, I’m afraid there is not currently anywhere to download the code as a whole. It is part of a very large project. The code in the snippets is just to illustrate the points in the text, it is not stand-alone code that will work in isolation (as you’ve found).

    The plan is to GPL the entire project in the future but not yet, it’s just not ready.

  14. demi on August 9th, 2007 5:52 pm

    Hi,Bart,

    Very well-written article.

    thx.

  15. Rechell on August 28th, 2007 6:00 pm

    Very good article. However, I tried using the simple indexed field validation for arrays of integers and it doesn’t seem to work…. Have you ever tired that?

  16. Bart B on August 29th, 2007 1:08 am

    Hi Rechell,

    I’ve personally only ever used this technique with Strings. However, it should work with all object types. The only question I could think to ask would be whether you’re using the literal type ‘int’ or the object type ‘java.lang.Integer’. To use the validator you need to use the latter, not the former. Sorry I can’t be of more help,

    Bart.

  17. Majid on November 9th, 2007 10:29 pm

    Hi,
    this is a good article, where can I find the code or the war file ?
    thanks

  18. Bart B on November 10th, 2007 3:34 pm

    Hi Majid,

    The only code available is the code included in the text of the Article.

    Bart.

  19. Alma Rivera on December 5th, 2007 6:41 pm

    More than a comment this is a doubt. I hope you can help or give some advice… In the team we are working with all use the same code, the same validation.xml but just in one of the compurers the javascript generated is working fine. In the others.. if I check the source generated for the validateRequired(form) what I see is:

    function validateUserFormBean(form) { if (bCancel)
    return true;
    else
    return validateRequired(form);
    }

    function required () {

    }

    And it is not finding the function, because what should be generated is function formName_required ()

    I hope you could help…

  20. BH on December 27th, 2007 11:49 pm

    Bart,

    Very nice write up.

    Are you using this indexedListProperty technique with client-side (Javascript) validation?

    This works for me using server-side validation, but I get an empty Javascript function attempting to use it client-side:

    function testFormBean_required () {
    }

    Any help greatly appreciated!

  21. Bart B on December 31st, 2007 1:11 pm

    Hi BH, I’m sorry to say I don’t use JS validation so I can’t be of any help here.

    Bart.

  22. Lisa on February 23rd, 2009 5:39 am

    Hi Bart B,
    I’ve been working on struts for a while now, but we’ve never used indexed properties. The only purpose I need it for is to preselect the select box after the action executes:

    However since the select box is within the logic iterate tag, I need to locate the particular select box from which the action was called. IN my action, I would like to do something like:

    requisitionForm.set(“groupVendorList”, tempVend);

    where tempVend is a String array of select values, however now, this causes all select boxes to be selected instead of just the one. Can I do what I need to using indexed properties?

    This is what I have in struts-config.xml

  23. Bart B on February 23rd, 2009 10:13 am

    Hi Lisa,

    Unfortunately I haven’t touched Struts in a few years now, so I can’t really answer your question. Hopefully someone else who’s following the comments here will be able to help.

    Bart.

  24. Vikram on August 26th, 2009 8:21 am

    Hi Bart,

    Thanks a lot for explaining the basic details in such a logical and lucid manner. I had issue with implementing String arraya in forms and by luck I found this articles. It made everything crystal claar and helped me to go home happily :):).

    Thanks again for this article.
    –cheers
    Vikram

Leave a Reply




Before you post a comment please remember that commenting on my blog is a privilege not a right. I won't approve comments that are obscene, offensive or insulting. For more info please read this post.

Subscribe without commenting