So it doesn't happen that often, but it just so happens I wrote what you
described today!  ;-)

You create a table with each row representing your child objects.  Each html
element should have the name parent.child[%{row.index}].childpropertyname. 
The row part comes from <s:iterator status="row">

Next you need to create a conversion file for Xwork to convert your field
arrays to objects in your collection.  This has the following properties:
KeyProperty_nameofchildproperty = id
Element_nameofchildproperty = child class
CreateIfNull_nameofchildproperty = true

What this will do is render your collection of child elements as a series of
editable fields which will map back to the objects in your parent.child
collection.  When you create your row of child fields it is important to
include the id of the child object.  

What is so cool about this solution is that its completely stateless.  No
need for funky session storage or some kind of temporary storage until you
have have everything submitted.  Everytime you post this form to the server
the collection is recreated for the parent based on the values in the
fields.  One caveat, if not every field on your model object is being edited
or submitted back as a hidden value then you should be loading your models
in a Prepare method of the Struts action.  This also means you need to add a
Params interceptor before the Prepare interceptor in the default Appfuse
interceptor stack in struts.xml.

Below is a complete example from one of the times I implemented this
pattern.  It also allows the user to add a new child by posting the form
back to the server, adding an empty object to the collection and going back
to the JSP template.  There are other ways to do this using
DHTML/AJAX/JSON/GWT/DWR/DOJO/YUI/REST/SOAP/EXT-UI and PHP but I think the
old skool stateless post is way cooler.  

This example uses a collection in the action but it works just as well if
the collection is a property of an other object being edited.  You would
just substitute equipmentCart references to parentObject.equipmentCart.   I
have an older example of the same thing where it was a Member object with a
Relationships collection.  So if the equipmentCart example is tough to map
to your domain let me know and I can dig up the old example.

You can still put the collection in the session if you wanted.  In the case
when the collection is a standalone that could be a good idea in case the
user navigates away from the page and then wants to come back and see their
draft collection still there.  The session code is commented out in the
Action.  The good news is that WebWork does care if its getting the
collection from the session or the value stack.  If they have the same name
it will pick it up from either.

****JSP Code****
 <%@ include file="/common/taglibs.jsp"%>
<html>
  <head>
      <title>Add Inventory Batch Listing</title>

      <script type="text/javascript">
          var FORM_NAME =       "equipmentCartForm";
          var CART_ADD_URL =    "addEquipment.html";
          var CART_POST_URL =   "postCart.html";
          var CART_EMPTY_URL =  "emptyCart.html";

          function post(url){
               var form = $(FORM_NAME);
              form.action = url;
              form.submit();
              return false;
          }
      </script>
  </head>
  <body>


  <div style="clear:both;margin-top:0pt;">
  <table id="cartTable" class="table" cellpadding="0" cellspacing="0">
      <caption>Items you wish to add to Inventory</caption>
      <thead>
          <tr>
              <th>Type</th>
              <th>Make</th>
              <th>Model</th>
              <th>Serial</th>
              <th>&nbsp;</th>
          </tr>
      </thead>
      <tbody>
          <s:form
            name="equipmentCartForm"
            id="equipmentCartForm"
            action="save"
            method="POST"
            theme="simple">
      <s:iterator value="equipmentCart" status="row">
            <tr>
                <td>
                    <s:hidden name="equipmentCart[%{#row.index}].id"
value="%{id}"/>
                    <s:hidden name="equipmentCart[%{#row.index}].version"
value="%{version}"/>
                    <s:select list="#attr.assetTypes"
                        name="equipmentCart[%{#row.index}].type"
                        value="%{type}"
                        id="equipmentCart[%{#row.index}].type"
                        />
<!--The name and value are different because of the way the iterator works. 
The object on the pushed to the top of the stack as the iterator works is
each Equipment object within the equipmentCart List.  So you just need to
pull the property off the current object.  There is a way to make a more
absolute reference using a #top reference.  I think I have done that before
but this is what I came up with this time-->
                </td>
                <td>
                    <s:textfield
                        name="equipmentCart[%{#row.index}].make"
                        value="%{make}"    
                        id="equipmentCart[%{#row.index}].make"
                        />
                </td>
                <td>
                    <s:textfield
                        name="equipmentCart[%{#row.index}].model"
                        value="%{model}"
                        id="equipmentCart[%{#row.index}].model"
                        />
                </td>
                <td>
                    <s:textfield
                        name="equipmentCart[%{#row.index}].serial"
                        value="%{serial}"
                        id="equipmentCart[%{#row.index}}].serial"
                        />
                </td>
                <td> remove.html?id=<s:property value= ">delete </td>
            </tr>
        </s:iterator>
          </s:form>
      </tbody>
      </table>
                   <div class="buttons">
                &lt;a href="javascript:post(CART_ADD_URL);"
                   class="positive"&gt;
                    Add Another
                &lt;/a&gt;
                &lt;a href="javascript:post(CART_POST_URL);"
                   class="positive"&gt;
                    Post
                &lt;/a&gt;
                &lt;a href="javascript:post(CART_EMPTY_URL);"
                   class="positive"&gt;
                    Empty
                &lt;/a&gt;
            </div> 
      </div>
  </body>
</html>


*****Action Code******
public class InventoryAction extends BaseAction implements Preparable {
    private Equipment equipment;
    private List<Equipment> equipmentCart;

    public void prepare() throws Exception {
       /* List<Equipment> sessionList = (List<Equipment>)
getSession().getAttribute("equipmentCart");
        if (getSession().getAttribute("equipmentCart") != null) {
            equipmentCart = sessionList;
        } else {
            equipmentCart = new ArrayList<Equipment>();
        }*/
        equipmentCart = new ArrayList<Equipment>();
    }

    public String addEquipment() throws Exception{
        equipment = new Equipment();
        equipmentCart.add(equipment);
        //loadCart(equipmentCart);  //-session load
        return SUCCESS;
    }

    public String postCart() throws Exception{
        return SUCCESS;
    }

    public String emptyCart() throws Exception{
        equipmentCart = null;
        //loadCart(equipmentCart); //-session load
        return SUCCESS;
    }

    private void loadCart(List<Equipment> equipmentCart){
        getSession().setAttribute("equipmentCart",equipmentCart);
    }



    public Equipment getEquipment() {
        return equipment;
    }

    public List<Equipment> getEquipmentCart() {
        return equipmentCart;
    }

    public void setEquipment(Equipment equipment) {
        this.equipment = equipment;
    }

    public void setEquipmentCart(List<Equipment> equipmentCart) {
        this.equipmentCart = equipmentCart;
    }
}


*****InventoryAction-conversion.properties  --- sits with the action .java
code*****
KeyProperty_equipmentCart=id
Element_equipmentCart=com.jmh.jmic.model.Equipment
CreateIfNull_equipmentCart=true


********struts.xml package*******************
    <package name="inventory" extends="default" namespace="/inventory">
        <action name="new" class="com.opensymphony.xwork2.ActionSupport">
           
<result>/WEB-INF/pages/inventory/inventory_new_master.jsp</result>
        </action>

        <action name="cart"
class="com.jmh.jmic.webapp.action.InventoryAction">
            <result>/WEB-INF/pages/inventory/inventory_add_list.jsp</result>
        </action>
        <action name="addEquipment"
class="com.jmh.jmic.webapp.action.InventoryAction" method="addEquipment">
            <result
name="success">/WEB-INF/pages/inventory/inventory_add_list.jsp</result>
        </action>
        <action name="postCart"
class="com.jmh.jmic.webapp.action.InventoryAction" method="postCart">
            <result
name="success">/WEB-INF/pages/inventory/inventory_add_list.jsp</result>
        </action>
        <action name="emptyCart"
class="com.jmh.jmic.webapp.action.InventoryAction" method="emptyCart">
            <result
name="success">/WEB-INF/pages/inventory/inventory_add_list.jsp</result>
        </action>
    </package>



Rob Hills wrote:
> 
> Hi All,
> 
> I have an AppFuse 2 + Hibernate + Struts application that includes two
> model classes with a one -> many entity 
> relationship.  For simplicity, I'll call them "Parent" and "Child" here.
> 
> I have a requirement to create a single form that allows the user to edit
> details of both the Parent and the related Child 
> records and then save the data to the database in a single transaction.
> 
> On the client side, I'm trying to work out whether to:
> 
> 1.    Post the child data in input arrays - one array for each Child
> attribute.
> 2.    Use Ajax in-place editing type techniques to return Child edits to the
> server.
> 
> On the server side, if I were to implement 1 above, I believe the standard
> Action methods would ensure that the whole post 
> was wrapped in a single Hibernate/DB transaction.  However, if I were to
> implement technique 2 (Ajax-based), would the 
> same apply?  If not, what would I have to do server-side to ensure all my
> db updates were wrapped in a single transaction?
> 
> Finally, is there some other client-side technique for solving this
> problem that I've not thought about?
> 
> TIA,
> Rob Hills
> Waikiki, Western Australia
> Mobile +61 (412) 904-357
> Fax: +61 (8) 9529-2137
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [EMAIL PROTECTED]
> For additional commands, e-mail: [EMAIL PROTECTED]
> 
> 
> 

-- 
View this message in context: 
http://www.nabble.com/AppFuse-2-%2B-Hibernate-%2B-Struts---Master-detail-form---update-master-and-detail-data-in-one-transaction-tf4802043s2369.html#a13740038
Sent from the AppFuse - User mailing list archive at Nabble.com.

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

Reply via email to