Link to home
Start Free TrialLog in
Avatar of PalmDrac
PalmDrac

asked on

Struts indexed html:Select tags not working

Hello Experts!
 
I'm using Struts and I'm trying to create some dynamic html:select dropdown lists. I say dynamic because these "select" controls should be rendered based on an arraylist of objects that is created in an action that setups the form. If this ArrayList contains two objects, then two html:select should be created, if contains three, then three are created and so on...
 
Since I don't know how many objects the ArrayList contains at runtime, I supposed that I needed something dynamic, since I need the selected value of each select list in the action that gets called when the form is submitted (and this property must be specified in the select tag). I checked the Struts docs and found out about the "Indexed" property of the html:select, I also found a doc at this URL: http://struts.apache.org/faqs/indexedprops.html that describes more or less what I need to do, but I tried to do it in the described way and it doesn't works, so I need help with this problem ASAP.
 
The code I'm using in the JSP this:
 

<logic:iterate id="caraciter" name="crearServicioForm" property="caracteristicas" indexId="ctr">
  <td class="cuerpotabla">
  <b><bean:write name="caraciter" property="descripcion"/></b><br><br>
  <html:select name="crearServicioForm" property="codValorSeleccionado" indexed="true">
   <html:optionsCollection name="caraciter" property="valores" value="id" label="descripcion"/>
  </html:select>
  </td>
</logic:iterate>

 
In the formbean "crearServicioForm" I have this:
 

The ArrayList that contains the objects over which we iterate is
private ArrayList caracteristicas = null;
 
The array that I created to store the selected value of each select is:
private String[] codValorSeleccionado = {};
 
      public String getCodValorSeleccionado(int i) {
            return codValorSeleccionado[i];
      }

      public void setCodValorSeleccionado(int i, String value) {
            this.codValorSeleccionado[i] = value;
      }

      public String[] getCodValorSeleccionado() {
            return codValorSeleccionado;
      }

      public void setCodValorSeleccionado(String[] codValorSeleccionado) {
            this.codValorSeleccionado = codValorSeleccionado;
      }

 
In the resulting html the selects are rendered like this:

<select name="crearServicioForm[0].codValorSeleccionado>
<select name="crearServicioForm[1].codValorSeleccionado">

 
(That's when the ArrayList contains two objects)
 
One thing that makes me curious is that the brackets are in the form, shouldn't it read crearServicioForm.codValorSeleccionado[i] ???
 
An interesting fact is that the page gets displayed without errors, thus I suppose that struts is finding a match between the codValorSeleccionado property in the page and the methods in the formbean. But it just doesn't works, no errors in the server console, but doesn't works.
 
I debugged and found out that in the action that gets called when submitting the page, the "getCodValorSeleccionado()" method of the formbean returns an empty array, thus the setCodValor... method that struts invokes is not filling the array.
 
In the document about indexed tags in the jakarta docs they talk about some way to handle "the wrinkle" of using indexed tags, in that you put the name of your member variable in the "name" atribute, but they show it with a html:text tag... I tried to do a similar thing with my select tag and the server reported an error of "codValorSeleccionado bean not found in any scope" which sound logical.
 
I also tried a method they described in which they use the IndexId of the iterate tag, and not the indexed of the html tag. Then they use a scriptlet to render that index in the property attribute. But that way doesnt work... it reports an arrayoutofbound error.
 
If you need any additional info or code let me know... but please give me some light on this.
 
Thanks.
 
PalmDrac
Avatar of siliconeagle
siliconeagle

We reading in indexed pages i use the following extension to Vector which creates a bea at a given index if it doesn't exist.
public class BeanVector extends Vector implements HttpSessionBindingListener{
    /**
       * Comment for <code>serialVersionUID</code>
       */
      private static final long serialVersionUID = 3515707269258088958L;
      private Class beanClass;
    /**
     * Constructor for BeanVector
     * @param initialCapacity
     * @param capacityIncrement
     */
    public BeanVector(int initialCapacity, int capacityIncrement) {
        super(initialCapacity, capacityIncrement);
    }

    /**
     * Constructor for BeanVector.
     * @param initialCapacity
     */

    public BeanVector(int initialCapacity) {
        super(initialCapacity);
    }

    /**
     * Constructor for BeanVector.
     */
    public BeanVector() {
        super();
    }
    /**
     * Constructor for BeanVector.
     * @param c
     */
    public BeanVector(Collection c) {
        super(c);
    }

    public BeanVector(Class c) {
        super();
        this.beanClass = c;
    }

    /**
     * Method getEmptyBeanClass. Creates a bean of the defind class (using the constructor of no parameters) and returns it.
     * @return Object
     */

    private Object getEmptyBeanClass() {
        Class[] classSignature = { };
        Object[] initArgs = {};
        Object o = null;
        try {
            o = this.beanClass.getConstructor(classSignature).newInstance(initArgs);
        } catch (Exception e) {
            //log.debug(this.getClass().toString() + ":" + e.getClass().toString() + ":" + e.getMessage());
        }
        return o;
    }



    /**
     * This get method checks if the defined bean is on the vector,
     * if not the vector is expanded to add the required indexes,
     * if there is no bean at the required index then a bean is created and set at the required index,
     *  the created bena is the passed back as in a normal get operation.
     * @see java.util.List#get(int)
     */

    public Object get(int i) {
        try {
            Object o = super.get(i);
            if (o == null) {
                super.set(i, getEmptyBeanClass());
            }
        } catch (ArrayIndexOutOfBoundsException ex) {
            while (!(super.size() > i)) {
                super.add(getEmptyBeanClass());
            }
        }
        return super.get(i);
    }



    public Object remove(int i) {
        Object o = null;
        try {
            o = super.remove(i);
        } catch (ArrayIndexOutOfBoundsException a) {
        }
        return o;
    }

      public void valueBound(HttpSessionBindingEvent arg0) {

      }

      public void valueUnbound(HttpSessionBindingEvent arg0) {
             for (int i=0;i<size();i++) {
            Object o=get(i);
            if (o instanceof HttpSessionBindingListener) {((HttpSessionBindingListener) o).valueUnbound(arg0);}
             }
      }
}

so if this vector is used in crearServicioForm in place of your ArrayList or String[] (for codValorSeleccionado) so it is initialised like this :-
private BeanVector caracteristicas = new BeanVector(String.getClass());
* you also need a getter/setter for the BeanVector;

but this also works where other beans are stored in the vector as well, not just strings.

Avatar of PalmDrac

ASKER

Well I solved the problem!

I was able to use the indexed property as I wanted it. This is the solution:

- I kept the form bean exactly as I showed in the original message, with the "indexed" methods and the common methods.

- I used the "indexId" attribute of the logic:iterate tag, with this I was able to have in a script variable with the index of the current iteration. I deleted the "indexed" attribute of the html:select tag. Then I used a small scriptlet to add the index to the name attribute of the select tag, that way each of the select tags that are dynamally generated will insert it's selected value into the given position of the codValorSeleccionado array.

The code of the iteration was changed as follows:

<logic:iterate id="caraciter" name="crearServicioForm" property="caracteristicas" indexId="ctr">
  <td class="cuerpotabla">
  <b><bean:write name="caraciter" property="descripcion"/></b><br><br>
  <html:select name="crearServicioForm" property="codValorSeleccionado" property='<%="codValorSeleccionado[" + ctr + "]" %>'>
   <html:optionsCollection name="caraciter" property="valores" value="id" label="descripcion"/>
  </html:select>
  </td>
</logic:iterate>

Now the real secret is that the codValorSeleccionado array must be intialized with some value in the action that prepares the JSP page, 'cause otherwise the server will throws an "ArrayOutOfBounds" exception. So to fix this I took the array and initialize it in the action with as many "0" as select:tags I was going to render in the page (this number is given by the amount of objects that are contained in the arraylist that generates the select:tags.

And that's it, it works.

Definitely the Struts documentation is very poor, 'cause I was not able to make indexed tags work as they described it in the docs.
yes what you have done should work as well - although index=true works with the modified vector above which is another way of avoiding ArrayIndexOutOfBoundsEx
ASKER CERTIFIED SOLUTION
Avatar of ee_ai_construct
ee_ai_construct
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial