rr-struts-nested.war Introduction
Sometimes when you are creating a form you might need to capture that is nested. For example, imagine a case where you were a sports team manager. You might want to edit a form that lists all your players and lets you change the starting position of each player on your roster all from one screen (instead of having to a single entry screen for each player.) In this case our backend might return to us a Collection of "Player" objects and in each Player object there is a "position" field. On our JSP we would need to set up a form that lets us edit each Player in the list.
In this lesson, we are sticking with our Employee theme. I'll only be going over parts of the code, so download the source in order to follow along. Also, this lesson assumes you understand the basics of Struts (If you don't go through at least the first three lessons).
This example isn't representive of what you might do in real life. A real life app would be much more robust, but it gives you the idea of how to work with Nested objects. In this example we are displaying a List of Employee objects and we can update each Employee including a List of Contact objects inside of each Employee.
Sort of unique in this example:
One of the interesting things about this example is that it does not use a Session scoped ActionForm, it uses Request scope. You'll see why this adds a bit of a challenge, but I think it's typically a good idea to only use the Session to store forms when you 'have' to - such as if you maybe had a wizard like flow, going from page to page capturing information before finally doing something with the data you've collected. Requirements
This application requires an application server that implements the Servlet 2.4 and JavaServer Pages 2.0 specifications. The examples should all work on Tomcat 5.x (Discussed in next section). Please do not e-mail about getting your application to run on a server other than Tomcat. The source code (and an Ant build file) is provided for all the lessons so you should be able to build a war from the source and run it on you application server of choice.
EmployeesForm
Our Employee only has one property to worry about "List employees." The reset method might look a bit unusual to some of you. First off, if you want avoid having this somewhat complicated reset method, you can do so by giving your Form session scope in the Actionm mapping. If you end up with a lot of nested lists inside of beans inside of lists, this is often an 'ok' solution because it does make things so much easier. Typically, you want to use Request scope for everything that you can however. (I try to only use Session based ActionForms for when I need to capture data across multiple page views - as in a wizard type flow of pages - imagine filling out your online taxes.)
But before we go any further, some of you might be wondering what the whole problem is between using Session or the Request in regard to nested objects in your form? The problem is that Struts has a lifecycle when a form submits. One of the steps in the process is to set all the form properties back to their inital values as declared in the form (usually this is just null unless we explicitly set a value). Then after they are all initiliazed, Struts goes ahead and tries to populate the properties which any Request parameters that match up. But what happens with Collection types and arrays? Struts will first initiliaze them but it has no idea about the size so the size will be 0 by default. This means when your form submits you will end up with IndexOutOfBounds errors. If you Session scope your form Struts will first make sure to set your properties with those values in Session scope, so in that case we'd be ok since the first time we setup our form (from our Action) the from would be populated with our Collections of whatever size and those same collections then get applied back during the struts lifecyle.
So what do we do when we want to use a Request scoped form that contains Collection type properties. Well besides initializing all our properties to their default values when a form submits, Struts will also always call the reset() method of the ActionForm before any Request parameters are applied to the form. Since we know this reset is always called, we could use it to our advantage. The key is that we need to make sure our Collection properties are setup with enough items in them to match what the JSP form will try to set. The easiest way to do this is to wrap our Lists in a LazyList which will grow our Collection whenever a mew item is added that is outside the Collection's size. So in my example I above I wrap my employees List in a LazyList and the Employee added to it also has LazyList wrappring the list of Contacts in Employee.
Things will become more clear if you read this wiki related to using LazyLists and how it relates to Struts forms.. http://wiki.apache.org/struts/StrutsCatalogLazyList
Using the Nested Tag
To display our nested information I'm using the nested tag. You could use JSTL alone to do it, but you code will look a lot messier. Remember you form inputs eventually need to look something like: <input type="text" name="employees[1].contacts[0].label" ... />. To do this with JSTL is a bit of a pain. For example here are two ways of Setting up nested form items.You can see how lon the html:text lines get for handling it the JSTL way, and this is only two deep. (I broke the lines in the example to not mess with this html width, in real life not sure the lines could be broken where I broke them.)
List of Department POJOs. Each Department has a List of Employees.Department properties: List employees, String deptName Employee properties: String empName, String email
|