I've run across a problem in the last couple applications I've worked on using Struts and I'm wondering if there's a better solution than what I have been using.
The problem I face is that I have a number of records in the database that all need to be displayed to a user. The user can then look at those records and edit any, all, or none of them. When the page is submitted, I need to take those changes and save them to the database.
In order to facilitate this in Struts, I've been using a Collection of objects in my form beans and then populating my JSP using the nested:iterate tag. That seems to work just fine.
However, it's reading the data back in that gives me trouble. It doesn't seem that Struts will create the objects from scratch - if they don't exist in the form bean, they simply aren't populated with the updated data. To get around this, I've been having my "preparation action" (the one that reads the data from the database) and my "submission action" (the one that stores the data back to the database) using the same form bean and I've set that form bean to use Session scope.
That approach seems to work, but I don't like keeping that sort of data in the session - it just doesn't seem appropriate.
What I usually do is use a what I call a Form Assembler object to move data back and forth between the form and my model. My model object will typically contain a property which is a collection of other objects. For example, let's say I have a Library object which contains a List of Book objects.
Given the use case you describe, I would have a LibraryFormAssembler which might look like this (maybe you have a FormAssembler interface to go along with it):
So if I wanted to view the List of Books, I might have this action
Edit the list (I'll assume you keep your Library object in session on this one)...
Add a blank book entry to the list of books so that it may be edited...
I could go on but I'm sure you probably get the gist by now. The point is I rebuild the form as needed using the assemblers. This makes sure that I always have a BookForm object to coincide with every Book object in my Library, which should alleviate the problem you have been running into, if I undertand correctly. [ June 21, 2005: Message edited by: Jason Menard ]
Thanks a lot for the suggestions. However, I've got a new wrinkle to add, if I may...
In an earlier case, I needed to grab data from a database and display that collection to the user. The user could then edit that data and resubmit it. The key here was that the number of elements that I'd be displaying would always be exactly the same as the number of element the user would be submitting. The user had no way to add or delete elements - only edit them and resubmit them. They were added and deleted through a different process.
In the case I'm now faced with, I may have some data to show the user originally, and I may not. The first time the user accesses the screen, there will not be any data to display. I'd like to use DHTML to allow the user to add new elements to add new elements to the form from the client side. Then, when the user is done, the user can submit those newly created elements. So, the trick here is that there are 0 elements going out and many elements coming back in. So, ahead of time, I have no idea how many elements to expect.
Honestly, the only way I can think to handle such a case is to do my own object population, rather than relying on Struts to do it for me. In that case, I can look through the request for values and create objects as needed. Anyone have a better approach to this problem?
Joined: Nov 09, 2000
I'd like to use DHTML to allow the user to add new elements to add new elements to the form from the client side.
Personally, I avoid doing this precisely because of the situation you are facing. That said, there are a few opitons:
1. Don't do it. Every time they "add" a new element your app would call an action that would do something like add a new empty object to your model's collection, then assemble the form based on the current state of the model. When you redisplay back to the user, he now has his blank element he can fill in and you have a one-to-one mapping of collection objects with your model. This is my preferred method.
2. Don't use an ActionForm. Have your Action (or a helper object used by the Action) parse through the request manually and decide how to populate your model. This one's pretty ugly imho and I'd avoid it.
3. Extend RequestProcessor and override processPopulate(). Examine the request and manipulate your ActionForm's collections as needed. Again, not my preferred option, hard to do generically so that it handles all ActionForms (say hello to my friend the Java Reflection API), but it is an opiton.
4. A combination of #3 along with having your ActionForms implement an interface which would make it easier for the RequestProcessor to modify your collections, but delegating the specifics to the ActionForm. For example maybe give your ActionForms a method name like addToCollection(String collectionName). Still, I don't particularly care for this option either.