Stephane,

it seems we're both working on the same problem. I just today posted a
similar question. And like yours, it seems my question was far too vague, as
I received not one reply yet... Let's see if we can help each other.

As I stated in my question, I've found this
(http://www.developer.com/java/ejb/article.php/2233591) article that
describes a solution to our problem. I however could not get it to work
using arrays either. Once I replaced in the form definition, in the action
and in the jsp the array by java.util.List it does work. I don't see any
reason to favor arrays over Lists, so this should probably help you too. I
however want to take it up one level higher, I want the dynamic form to go
into a tile. i.e. I don't want my data stored in session any more. But first
let me see if I can help you by giving you my code.

my form definition (from struts-config.xml):

        <form-beans>
                <form-bean name="searchForm"
type="org.apache.struts.action.DynaActionForm">
                        <form-property name="table" type="java.lang.String" />
                        <form-property name="criteria" type="java.util.List" />
                        <form-property name="columns" type="java.util.List" />
                        <form-property name="results" type="java.util.List" />
                </form-bean>
....

I'm creating a generic search form that I can use for all my apps entities;
table receives the name of the set of search criteria and criteria receives
the criteria themselves. Like in your case, this list contains the entities
with the properties repeated in the form.


My bean (i'll leave out the getters and setters as these do not add anything
to the story):

/*
 * Created on 6-apr-2005
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package nl.krew.va.search;

import java.io.Serializable;

/**
 * CriteriumLine bean for holding exactly one criterium in the search form.
 *
 * @author Richard
 *
 */
public class CriteriumLine implements Serializable {

    /**
     * The key for the label to show in front of this criterium.
     */
    private String label = null;
    /**
     * The name of the property that this criterium checks against.
     */
    private String property = null;
    /**
     * A code for the operator to use in the check. i.e. something like
     * EQ for equal, GT for greater than etc.
     */
    private String operator = null;
    /**
     * Place holder for the values to check the property against as
     * entered by the end user.
     */
    private Object[] values = null;

..... (left off the rest)




And now the trick part, as I've taken it of James' article: In the action
mappings I defined
2 actions. The first that calls my first action SearchAction and then
forwards into a view (in my case a tile definition, search.page, but this
eventually evaluates to searchTile.jsp), the second that calls the second
action ListAction which tries to actually retrieve the results and pass
them, together with the criteria to the search.page again. As you can see
I'm trying to be clever with the wildcard mapping, but that doesn't work as
I want it to do yet either :(.

        <action-mappings>

                <!-- Entry point for the search pages. Instantiates the
                        DynaActionForm searchForm, fills the table property 
(from
                        the first part of the url used) and expands and
                        initializes the criteria array according to what is 
needed
                        for that table. -->
                <action path="/*Search"
                                type="nl.krew.va.actions.SearchAction"
                                parameter="{1}"
                                name="searchForm">
                        <forward name="success" path="search.page" />
                </action>

                <!-- Post target for search.page. Lists, if possible,
                        the results of the search. Redirects back into the
                        search form otherwise -->
                <action path="/Search"
                                type="nl.krew.va.actions.ListAction"
                                name="searchForm"
                                input="search.page">
                        <forward name="success" path="results.page" />
                </action>
....



Now, For the first action, SearchAction. What this does is to store the
table name in the searchForm, then find the search criteria for that table
(using business layer calls) and re-dimension the searchForm's criteria List
by adding CriteriumLine entries.

/*
 * Created on 6-apr-2005
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package nl.krew.va.actions;

import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import nl.krew.va.Constants;
import nl.krew.va.search.CriteriumLine;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.DynaActionForm;

/**
 * Retrieves the search conditions defined for the table specified in
'parameter'. Then passes the
 * found screen definition to the search view.
 *
 * @author Richard
 */
public class SearchAction extends Action {

   private static Log log = LogFactory.getLog(SearchAction.class);


    /* (non-Javadoc)
     * @see
org.apache.struts.action.Action#execute(org.apache.struts.action.ActionMappi
ng, org.apache.struts.action.ActionForm,
javax.servlet.http.HttpServletRequest,
javax.servlet.http.HttpServletResponse)
     */
    public ActionForward execute(
                ActionMapping mapping,
                ActionForm form,
                HttpServletRequest request,
                HttpServletResponse response)
            throws Exception {

        if (log.isTraceEnabled())
            log.trace("Entering SearchAction.execute.");

        DynaActionForm searchForm = (DynaActionForm) form;

        String table = mapping.getParameter();
        searchForm.set( Constants.SEARCH_TABLE, table);

        // TODO: make BF call to retrieve CriteriumLines for the table.
        List criteria = new ArrayList();
        criteria.add( new CriteriumLine("IncidentID"));
        criteria.add( new CriteriumLine("bla bla bla"));

        searchForm.set( Constants.SEARCH_CRITERIA, criteria);

        if (log.isInfoEnabled())
            log.info( "Search on  [" + table + "] has  " + criteria.size() +
" criteria.");

        if (log.isTraceEnabled())
            log.trace("Leaving SearchAction.execute.");

        return mapping.findForward(Constants.FWD_SUCCESS);
    }
}


And the second action, ListAction. This takes the table plus the entered
search criteria and performs the search. The results are then stored,
together with a definition of the columns that should be shown to the end
user, in the listForm.

/**
 * Retrieves the search criteria from the searchForm and tries to get a list
 * of entities for the search results.
 *
 * @author Richard
 */
public class ListAction extends Action {

    private static Log log = LogFactory.getLog(ListAction.class);


    /* (non-Javadoc)
     * @see
org.apache.struts.action.Action#execute(org.apache.struts.action.ActionMappi
ng, org.apache.struts.action.ActionForm,
javax.servlet.http.HttpServletRequest,
javax.servlet.http.HttpServletResponse)
     */
    public ActionForward execute(
            ActionMapping mapping,
            ActionForm form,
            HttpServletRequest request,
            HttpServletResponse response)
    throws Exception {

        log.trace("Entering ListAction.execute.");

        DynaActionForm listForm = (DynaActionForm) form;

        String table = listForm.getString( Constants.SEARCH_TABLE);
        List criteria = (List)listForm.get( Constants.SEARCH_CRITERIA);

        log.debug( "Creating result list for table [" + table + "].");

        ListBF bf = new ListBF();
        listForm.set( Constants.SEARCH_COLUMNS, bf.getListColumns( table));
        listForm.set( Constants.SEARCH_RESULTS, bf.getList(table /* TODO:
Add selection criteria.*/ ));

        log.trace( "Leaving SearchAction.execute.");
        return mapping.findForward( Constants.FWD_SUCCESS);
    }
}


Then, the view, as implemented in searchTile.jsp. This is far from finished,
but the idea should be visible already. As an improvement over your version,
this does not need the nb field, it just iterates over the number over
entries in the list.

<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>

<%-- Search Tile

        This tile renders a form in which the user can enter selection criteria
        for the specified table.
        It takes as parameter the table and a list of criteria. Each item is a
        bean with following properties :
                label - key for the message to show in front of the criterium.
                property - name of the property to select on.
                operator - code for the comparison to perform.
                values - array of the values entered by the end user (dimension 
may
                        vary upon the operator entered).



        @param table the table to render a search form for.
        @param criteria List of CriteriumLine beans.
        @param columns ignored.
        @param results ignored.
 --%>


<%-- Push tiles attributes into page context --%>
<tiles:importAttribute />


<logic:present name="title">
        <em><tiles:getAsString name="title"/></em>
</logic:present>

<html:form action="/Search.do" >

<html:hidden property="table"/>

<table>
        <logic:iterate
                id="criterium"
                name="searchForm"
                property="criteria"
                type="nl.krew.va.search.CriteriumLine"
                indexId="indexId" >

                <tr>
                        <td>
                                <html:hidden name="criterium" 
property="property" indexed="true"/>
                                <bean:write name="criterium" property="label"/>
                        </td>
                        <td>
                                <html:text name="criterium" property="operator" 
indexed="true"/>
                        </td>
                        <td>
                                <html:text name="criterium" property="values" 
indexed="true" />
                        </td>
                </tr>
        </logic:iterate>
</table>

<html:submit />

</html:form>



The ListTile.jsp is not yet there yet. But it is just going to be more of
what happened in searchTile.jsp.


The problem I am now facing is that for this instance of the searchForm to
still exist
later on in the both .jsp's and the ListAction, the form needs to be stored
into session
scope. If I don't store it there, a new instance will be created upon every
call and I
will be given an empty List for criteria every time. However, since I want
to make the
pages into a tile I don't want the searchForm to go into session scope.

I hope this does help you, Stephane. And of course, any suggestions are
welcome (from anyone).

Richard


-----Oorspronkelijk bericht-----
Van: Stéphane Zuckerman [mailto:[EMAIL PROTECTED]
Verzonden: donderdag 7 april 2005 18:35
Aan: Struts Users Mailing List
Onderwerp: Re: LazyLists and dynamic forms


Niall Pemberton a écrit :
> Its hard to help when all you say is "failed to make their examples work".
> Posting snippets of relevant bits of your struts-config.xml, jsp etc would
> help along with what actually happened.

Sorry for the lack of details, I know I was really too vague.

I am using Struts 1.2.4 .

So, here is what I want to do :
I have a list of text fields which number is unknown when the user gets
to the JSP. Fields are added dynamically through JavaScript.

Now, I know there is Struts 1.2.6 on its way, but I can't wait for it to
get a "stable" label.

So here is the example I was trying to follow (extracted from
http://wiki.apache.org/struts/StrutsCatalogLazyList) :

[quote]
In fact using Arrays (rather than Lists) a LazyDynaBean can be used
directly, in the following way:

   <form-beans>

      <form-bean name="skillForm"
                type="org.apache.commons.beanutils.LazyDynaBean">
         <form-property name="skills" type="myPackage.SkillBean[]"/>
      </form-bean>

   </form-beans>
[/quote]

Supposedly, the LazyDynaBean object should be wrapped within a
BeanValidatorForm one.

Here is what I am using in my struts-config.xml file :

       <form-bean name="dynaLazyForm"
                 type="org.apache.commons.beanutils.LazyDynaBean">
         <form-property name="nb" type="java.lang.String" />
         <form-property  name="persons"
                        type="net.beuarh.lasher.Person[]"/>
       </form-bean>

The class Person is really just a bean, with deux properties : name and
fname.

Here is the JavaScript code I use to generate new text fields :

function foo() {
        var nb = document.getElementById('nb').value;
        var f = document.getElementById('f');
        f.innerHTML = null;
        f.innerHTML = "nb: "+
                "<input type='text' id='nb' value='"+nb+"' /><br />";
        for (var i = 0; i < nb; i++) {
                f.innerHTML += "name: <input type='text' " +
                      "name='persons["+i+"].name' id='name' /><br />";
                f.innerHTML += "first name:<input type='text' " +
                      "name='persons["+i+"].fname' id='fname' /><br />";
        }
        f.innerHTML += "<input type='submit' value='submit' />";
}

The JSP is really basic (since this is a test) :

<html>
   <head>
     <html:base/>
     <title>JSP for lazyForm form</title>
     <script src="js.js" language="javascript"
             type="text/javascript"></script>
   </head>
   <body>
     <html:errors/>
     <html:form action="/dynaLazy" styleId="f">
       nb : <html:text property="nb" onblur="foo()" styleId="nb"/>
       <br />
       <html:submit/>
     </html:form>
   </body>
</html>

... And my Action class is as simple as the JSP :

public final class DynaLazyAction extends Action {
        public ActionForward execute(...) {
                DynaValidatorForm dynaForm = (DynaValidatorForm) form;
                // here is the line that makes it all crumble...
                Person[] persons = (Person[]) dynaForm.get("persons");

                request.setAttribute("persons",persons);
                return mapping.findForward("ok");
        }
}

As I said before, when it is about "manual" forms, everything's fine, I
use LazyLists, and it works (thank you so much, Rick; you made my
morning wonderful :-) ).

I hope I wasn't too messy in my explanations ...

Thanks for any clue you might give me :-)

--
Stéphane Zuckerman

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]




---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to