Search code examples
javaiterationform-submitstruts-1

Struts 1 - Iteration in Iteration - Inner row method


I have a logic:iterate in a logic:iterate. I can edit all fields in this table. For every logic:iterate i have a getXXX row method.

First I click on my ´add´ button. The iteration filled with 3 persons. First have one address, second have two and third have three. (In real application it is dynamic)

Now I change some input fields and click on my send button. Now the row methods are called for initialization the old state. But how I implement the row address method right? It will be called only three times but I must exactly fill the persons fields and get them back, or?

Hope its a better description for my problem as in the last thread.

EDIT: Try to explain it better. I have ONE personlist and in my getRowPerson- method there is no problem because the "index" is only from THIS list. BUT now i have in this example three adresslists, i cant relate which "list" calls the getRowAdress. (At this point i must return a address from the right person with the right index, but i don't know the person only the position in adresslist)

If the getRowAdress were in the person class it is distinct, too, but there it never called.

The other problem is that the getRowAdress is only called three times, in my opinion, i should be called for every address row item once. For my example six times. I think there is something wrong with the inner iterate. I tried out nested:iteration but doesnt work, too.

Console:

adress index2<br>
adress index1<br>
adress index0<br>
person index1<br>
person index2<br>
person index0<br>

Html:

<logic:iterate id="rowPerson" name="iterateForm" indexId="rowIndex" property="persons" >
    <html:text name="rowPerson" property="name" indexed="true"/>
    <logic:iterate id="rowAdress" name="rowPerson" indexId="rowAdressIndex" property="address" >
        <html:text name="rowAdress" property="addressname" indexed="true"/>
    </logic:iterate>
    <br>
</logic:iterate>
<html:submit value="Send" property="submitvalue"/>
<html:submit value="Add" property="submitvalue"/>
</html:form>

Action

public class HelloWorldAction extends Action {

    @Override
    public ActionForward execute(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) throws Exception {
        HelloWorldForm helloWorldForm = (HelloWorldForm) form;
        String submitvalue = request.getParameter("submitvalue");
        if (submitvalue == null) {
            return mapping.findForward("success");
        } else if ("Add".equals(submitvalue)) {
            for (int i = 0; i < 3; i++) {
                PersonForm person = new PersonForm();
                person.setName("Person " + i);
                for(int y = 0 ; y <= i ; y++){
                    AddressForm add = new AddressForm();
                    add.setAddressname("Adress" + y);
                    person.getAddress().add(add);
                }
                helloWorldForm.getPersons().add(person);
            }                   

        }
        return mapping.findForward("success");
    }
}

ActionForm

public class HelloWorldForm extends ActionForm{
    private static final long serialVersionUID = -473562596852452021L;

    private List<PersonForm> persons = new ArrayList<>();

    public PersonForm getRowPerson(int index){      
        System.out.println("person index" + index);
        while(persons.size() <= index){
            persons.add(new PersonForm());
        }           
        return persons.get(index);
    }

    public AddressForm getRowAdress(int index){
        System.out.println("adress index" + index);
        // ???? dont know ???
        return new AddressForm();
    }

    public List<PersonForm> getPersons() {
        return persons;
    }

    public void setPersons(List<PersonForm> persons) {
        this.persons = persons;
    }
}

Personform and Addressform have only setter & getter


Solution

  • Your JSP is rendering this HTML code:

    <input type="text" name="rowPerson[0].name" value="Person 0">    
    <input type="text" name="rowAdress[0].addressname" value="Adress0">
    

    This triggers calls to getRowPerson(0) and getRowAdress(0) in your HelloWorldForm. But we actually want to nest the elements, and for that we must use the nested tag library:

    <nested:iterate property="persons">
        <nested:text property="name"/>
        <nested:iterate property="address">
            <nested:text property="addressname"/>
        </nested:iterate>
        <br>
    </nested:iterate>
    

    This generates:

    <input type="text" name="persons[0].name" value="Person 0">
    <input type="text" name="persons[0].address[0].addressname" value="Adress0">
    

    Using the same method to read from and write to the lists simplifies the solution. If the scope in your action is set to session, which is the default, then your form will have the values overwritten after you submit and you don't need to worry about dynamically growing the lists. You can simply have:

    public class HelloWorldForm extends ActionForm{
        private List<PersonForm> persons = new ArrayList<>();
    
        public List<PersonForm> getPersons() {
            return persons;
        }
        public void setPersons(List<PersonForm> persons) {
            this.persons = persons;
        }
    }
    

    Otherwise, if you have scope="request" in your action mapping, in the struts-config.xml, you will need to recreate the lists dynamically. For this, an option is using the commons-collection LazyList:

    public class HelloWorldForm extends ActionForm {
        private final Factory personFactory = new Factory() {
            @Override public Object create() { return new PersonForm(); }
        };
        // commons-collection 4 offers a LazyList<E> with generics
        private List lazyPersons = LazyList.decorate(new ArrayList(), personFactory);
    
        public List<PersonForm> getPersons() {
            return lazyPersons;
        }
        public void setPersons(List<PersonForm> persons) {
            this.lazyPersons = LazyList.decorate(persons, personFactory);
        }
    }
    

    (implement code like above for PersonForm too)

    Needing dynamically growing lists is an usual Struts problem. Here's a catalog of solutions for this, if you want to read further: http://wiki.apache.org/struts/StrutsCatalogLazyList