It looks like trying to use Struts’ nested indexed properties in a form with table-layout, with every line (a few formfields) as an editable separate entity, has driven a lot of people (including me) nuts.
With nothing about it in the manual, and no answers to be found on the web, here’s finally a solution.
The JSP:
<logic:iterate name="orderForm" property="orders" id="rememberMe"> <html:text name="rememberMe" property="fieldA" indexed="true"/> </logic:iterate>
will create a list in HTML like
<input type="text" name="rememberMe[ 0 ].fieldA"> <input type="text" name="rememberMe[ 1 ].fieldA">
([0] and [1] being the index, which is added automatically (the ‘indexed=”true”‘ attribute)
The formbean of course needs the collection (assuming there is a Line class/mini-formbean, having fieldA as membervariable with a public getter):
private List orderLines; public List getOrderLines() { return orderLines; } public void setOrderLines(List orderLines) { this.orderLines = orderLines; }
but also, here is the trick, a method with a special name that returns an indexed
element (orderline) from the collection of orders:
public Line getRememberMe(int index) { return (Line)orderLines.get(index); }
As you see, the name of the method needs to correspond with the ‘id’ attribute in the
logic:iterate tag and the ‘name’ attribute in the html:text tag.
This looks like it’s going to work smoothly, but there’s still one problem left.
Everytime a client posts a new request he gets a new (resetted) formbean, so the
collection is not initialized at the moment the form-values will be copied into the bean.
Normally this is ok, but since Struts (or commons.BeanUtils, actually) calls the
“getRememberMe” method to get an element from the collection to set a value in one of
its fields, this can result in a NullPointerException
or ArrayIndexOutOfBoundsException
.
There are of course multiple solutions for this, among which:
- If you know the maximum amount of orderlines you will have in your form, you could pre-construct
them all in the constructor of the formbean, using default values. - An automatically growing collection that creates, initializes and adds a new element to
the collection if the getter is called. This might feel weird at first, but i do think
it is the best solution. It has the additional plus that a dynamically growing form
(for example by using the javascript/DOMcloneNode
method) will always fit in the collection.
This class could look something like this:
public class AutoGrowingList extends ArrayList { private Class clazz; public AutoGrowingList(Class clazz) { super(); this.clazz = clazz; } public Object get(int index) { Object obj = super.get(index); if (obj == null) { obj = clazz.newInstance(); super.add(obj); } return obj; } }
Can we use this DyanaAction Form?
I would be thanked, if you post an example with a collection inside other collection, with indexed=true.
My problem it’s when i fill the text inputs, then submit form and some error occurs, struts can’t populate my collections… 😐 (yes my lists are instanciated)
When i search for this issue, i found this. Eventhough it is a old one, as i find the solution in another way, i want to present it.
Consider a bean named PersonVO. Now you have an array of PersonVO objects.
Set it to the form as create a instance variable PersonVO[] arrPerson = new PersonVO[10]. Initialize with default size or follow the steps that presented in the forum earlier.
In jsp page, when you iterate this, use the id name same as the name declared in the form.
Now if you change the first name, the in action class if you get the arrPersons object, you will get the updated one.
There is a comment wrote by Stephen Montgomery on comment-6971 that says..
“I have a collection in collection situations. Where my form bean has a collection of objects (deosetBeans, here) and they have an attribute which itself is a collection (deoBeans, here); which I wanted to use in a form. I implemented the get/set methods as above in both the bean classes for the objects in the collections and then ran the JSP like this:
All was gravyâ€
I have exactly the same problem as mentioned on there, and I can’t resolve the problem, if someone has the answer please let me know, or tell me where can I read more about it.
I will appreciate any help..
I just noticed that the html tags were swallowed up.
In my earlier post, i meant that the HTML:IMAGE tag does not support the ‘name’ attribute as the HTM:TEXT tag .
How do i resolve a similar problem ….Along with the indexed text fields i also have to generate a Delete button (ImageButtonBean) for each row. The tag does not support the ‘name’ attribute as the . So, I am stuck with the ArrayIndexOutofBoundException where my buttons are concerned.
I am at a loss here as to how to resolve this. Does someone have an answer for this?
This is more related to the above mentioned problem.
How do we proceed if we have a form with two different collections under the same form. Both the collection elements need to be mapped to the form elements on the UI, they should be editable and the list can grow as well.
the problem above was caused by method Order.setName() being not public
I’m having problem with setter methods.
The html contains something like
<input type=”text” name=”order[0].name” value=””>
<input type=”text” name=”order[1].name” value=””>
I have defined both
Order getOrder(int index)
and
void setOrder(int index, Order order)
methods in my form bean, but unfortunately only the getter is called, the setter does not ever get called, so the submitted indexed values are lost. I suppose I can set them via request.getParameter(), but isn’t there any other way? I’m using Struts 1.2.9
(cont’d)
for (int i = 0; i
public class AutoGrowingList extends ArrayList
{
private Class clazz;
public AutoGrowingList(Class clazz)
{
super();
this.clazz = clazz;
}
public Object get(int index)
{
int size = this.size();
if (index >= size)
{
for (int i = 0; i
Kindly pls get me the Code so that i can work. I am having problem in setting values.
Is it possible to use indexed properties in a dynaActionForm?
Wonderful! It has taken me all day to understand why i was getting a null pointer exception, like you say nothing is documented – not even on the oft-mentioned “struts”/faq/indexprops.html page. Now at last i fully understand
Thank you for making this public.
All the posts on this page (including the original) make a dangerous assumption, which is the web browser will return all the Line objects in the order that Struts wrote them to the JSP. If it does not, at best the objects will be out of order, and at worst List objects will overwrite each other and data will go missing. Isn’t preserving order the whole point of indexing?
example problem:
rememberMe[1] is put into the Request first, followed by rememberMe[0].
In this case, rememberMe[1] flows through List.add() because its index (1) is NOT in the list (size zero). It is followed by rememberMe[0] which is retrieved, not added, because its index (0) IS now in the list (size 1). Unfortunately rememberMe[1] is retrieved and overwritten with the contents of rememberMe[0].
the AutoGrowingList.get() function in the original post should be modified to:
public Object get(int index) {
if(index >= super.size()) {
for(int i=0; i
Just to be clear, the above method requires care around the reuse of collections across requests because of the single default object – the below implementation is a little less error prone:
public interface InstanceFactory {
/** Get an instance of the class this generates */
T getInstance();
}
public final class AutoGrowingList extends java.util.ArrayList {
/** Because
ArrayList
is serializable. */private static final long serialVersionUID = 3256722870804559412L;
/** The object that will be used to fill out the list if necessary */
private final InstanceFactory instanceFactory;
/**
* Sole constructor.
*
* @param defaultObject the object that will fill out the list if necessary
*/
public AutoGrowingList(final InstanceFactory instanceFactory) {
this.instanceFactory = instanceFactory;
}
public final T get(final int index) {
while (this.size()
This is a great solution to the problem of entering an unlimited number of elements in a struts form, which I am using on my site. However, if you are using java 5, the following genericised version is type safe, and can’t throw instantiaion errors or access errors if used carelessly. I hope this is of use to people:
import java.util.ArrayList;
/**
* An
ArrayList
that grows automatically, so that it never throws an*
ArrayIndexOutOfBoundsException
. In the case where the list needs to grow,* it will add the default object passed in to the constructor until the list is the
* required size.
*
* @author Mark Slater
*/
public final class AutoGrowingList extends ArrayList {
/** Because
ArrayList
is serializable. */private static final long serialVersionUID = 3256722870804559412L;
/** The object that will be used to fill out the list if necessary */
private final T defaultObject;
/**
* Sole constructor.
*
* @param defaultObject the object that will fill out the list if necessary
*/
public AutoGrowingList(final T defaultObject) {
this.defaultObject = defaultObject;
}
public final T get(final int index) {
while (this.size()
Iam confused with this code
Author is using property=”orders” in the above code..And using the following code. where he referss
it as orderLines
private List orderLines;
public List getOrderLines() { return orderLines; }
public void setOrderLines(List orderLines) { this.orderLines = orderLines; }
If some one understood it properly please help me..
Thanks In Advance
Priya
Hi,
It is a very good example. It really helped me in solving the problem.
Make sure you have getxxxxxxx(int index) implemented properly. Otherwise when form is submitted you may get errors.
The above mentioned one “getCurrUnitAsgnRule(int index)” example solved my problem.
How do you make this work with nested c:forEach loops? My inner loop has correctly added [n] to each bean, but it doesn’t account for the outer loop variables. I have 4 levels of nested loops.
Thanks and good post.
Steve
I mean with c:foreach tag.
-Pavel
Nice article! in JSP?
Is it possible to use this approach with EL tag
Pavel
Hi..
Its excellent. But I am facing a small problem.
The collection is getting submitted, but some of the attributes of objects in collection
are coming null !!! Has anybody faced the same problem. Please guide !!!
Here are some results
RecIndex :0
Status :-1 Sub To Client : clientdate0 Interview Date : interdate0 Date Started : startDate0
Status :-1 Sub To Client : clientdate1 Interview Date : interdate1 Date Started : startDate1
Status :-1 Sub To Client : clientdate2 Interview Date : interdate2 Date Started : startDate2
Status :-1 Sub To Client : clientdate3 Interview Date : interdate3 Date Started : startDate3
Status :-1 Sub To Client : null Interview Date : interdate4 Date Started : startDate4
Status :-1 Sub To Client : clientdate5 Interview Date : interdate5 Date Started : startDate5
Status :-1 Sub To Client : clientdate6 Interview Date : null Date Started : startDate6
Status :null Sub To Client : clientdate7 Interview Date : interdate7 Date Started : startDate7
Status :null Sub To Client : clientdate8 Interview Date : interdate8 Date Started : startDate8
Status :null Sub To Client : clientdate9 Interview Date : interdate9 Date Started : startDate9
Rahul
Hi..
Its excellent. But I am facing a small problem.
The collection is getting submitted, but some of the attributes of objects in collection
are coming null !!! Has anybody faced the same problem. Please guide !!!
Rahul
Excellent! I was unable to resolve this nested indexing issues for long time. This article really helped me
//here’s the form Bean
public Collection getUnitsAssignmentsRules() {
return unitsAssignmentsRules;
}
/**
* @param collection
*/
public void setUnitsAssignmentsRules(Collection collection) {
unitsAssignmentsRules = collection;
}
//UnitAssignmentRule is bean which represents one record on screen
//Getter Method
public UnitAssignmentRule getCurrUnitAsgnRule(int index){
while(index >= unitsAssignmentsRules.size()) {
unitsAssignmentsRules.add(new UnitAssignmentRule());
}
return (UnitAssignmentRule)((ArrayList) unitsAssignmentsRules).get(index);
}
You don’t need the setter method for each collection. you can set the collection as you retrieve from db.
JSP implementation –
Great! Good to know this solution is working for so many of you
I have a collection in collection situations. Where my form bean has a collection of objects (deosetBeans, here) and they have an attribute which itself is a collection (deoBeans, here); which I wanted to use in a form. I implemented the get/set methods as above in both the bean classes for the objects in the collections and then ran the JSP like this:
All was gravy
td>
size="10"maxlength="10"/>
table>
I feel like i lost about three days of my life trying to sort this problem out! I’d got to the ‘NullPointerException’ step but couldn’t work out what I needed to do next! Needless to say your solution has helped me to reclaim my sanity.
Many many thanks!
If I submit the form will I have chnaged values in my nested bean inside the arraylist though?
Nice work!
Very good!This is the firts article which I find the solution….Spain
If you sent us the code (weblog@amis.nl) we will put it in a seperate post under your name if you like
Can’t post the HTML and jsp sourch
//JSP
l o g i c : iterate id=”lineRecord” name=”MyActionForm” property=”listRecords” type=”MyBean”
h t m l : text indexed=”true” name=”lineRecord” property=”propOne”
Oops … This forum doesn’t allow post of html / jsp code ! Trying again. sorry for above.
//JSP
…
< logic : iterate id="lineRecord" name="MyActionForm" property="listRecords" type="MyBean">
….
//HTML
….
< input type="text" name="lineRecord[0].propOne"/ >
< input type="text" name="lineRecord[1].propOne"/ >
< input type="text" name="lineRecord[2].propOne"/ >
….
This post was usefull to me. I have merged all and giving the working set of code for using
dynamic, indexed and nested property with struts. I hope you will find this usefull.
//jakarta-struts-1.2.4
//MyActionForm.java
…
private List listRecords;//dynamic, nested and indexed property
public SurchargeDiscountActionForm() //Constructor {
listRecords = new ArrayList();
}
//MyBean is bean which represents one record on screen
//Getter Method
public MyBean getLineRecord(int index) {
while(index >= listRecords.size()) {
listRecords.add(new MyBean());
}
return (MyBean) listRecords.get(index);
}
//Setter Method
public void setLineRecord(int index, MyBean object) {
if (index
….
//HTML
….
….
i spent almost 3 weeks trying to figure nested tag(Struts) out..thanks for this post..good post..
Here is the form i crated using a LazyList
in Action Form :
protected List deviceList = LazyList.decorate(new ArrayList(), this);
.
.
.
/**
* @return Returns the deviceList.
*/
public List getDeviceList() {
return deviceList;
}
public void setDeviceList(List list) {
this.deviceList = list;
}
public LabelValueBean getDeviceList(int index){
return (LabelValueBean)deviceList.get(index);
}
/* (non-Javadoc)
* @see org.apache.commons.collections.Factory#create()
*/
public Object create() {
return new LabelValueBean(“”,””);
}
public void addDevice() {
deviceList.add(new LabelValueBean(“”,””));
}
in JSP:.submit();’>[+](Add new Device)
.method.value=”addDevice”;document.