Editting of Command object containg collection obj

2019-09-07 05:47发布

I am working with spring MVC.My command object contains a collection object,it looks like as follows

public class DimensionStoneBean {
int stoneNo;
float length;
float breadth;
float height;
float dimension;
String isIssued;

public int getStoneNo() {
    return stoneNo;
}
public void setStoneNo(int stoneNo) {
    this.stoneNo = stoneNo;
}
public float getLength() {
    return length;
}
public void setLength(float length) {
    this.length = length;
}
public float getBreadth() {
    return breadth;
}
public void setBreadth(float breadth) {
    this.breadth = breadth;
}
public float getHeight() {
    return height;
}
public void setHeight(float height) {
    this.height = height;
}
public float getDimension() {
    return dimension;
}
public void setDimension(float dimension) {
    this.dimension = dimension;
}
public String getIsIssued() {
    return isIssued;
}
public void setIsIssued(String isIssued) {
    this.isIssued = isIssued;
}

}





    public class UpdateStockBean {
@SuppressWarnings("rawtypes")
private List dimensionStones =
    LazyList.decorate(new LinkedList(),FactoryUtils.instantiateFactory(DimensionStoneBean.class));
long openbalance;

public UpdateStockBean() {
    super();
}

@SuppressWarnings("rawtypes")
public List getDimensionStones() {
    return dimensionStones;
}
public void setDimensionStones(@SuppressWarnings("rawtypes") List dimensionStones) {
    this.dimensionStones = dimensionStones;
}
public long getOpenbalance() {
    return openbalance;
}
public void setOpenbalance(long openbalance) {
    this.openbalance = openbalance;
}

}

The controller class for this,extends AbstractWizardFormController

I have used formBackingObject() to populate the command object return it to form

The form looks like as follows

  <form:form commandName="updateStock" method="post" name="stockEntry" action="updateStock.nic" id="updateStock">
  <br><br><br>
  <table border="1" width="700">
   <tr>     
     <td class="textClr1" align="left" width="50"><nobr>Total No Of Stones</nobr></td>
     <td colspan="6"><form:input tabindex="1" path="openbalance" id="openbalance" cssClass="controlStock"/></td>
   </tr>    
   <tr>
      <td></td>
      <td colspan="6"><form:errors cssClass="error" path="openbalance"/></td>
   </tr> 
  </table>
  <table>
   <tr>
     <td colspan="3">
        <table  border="1" width="400">
          <tbody id="dimensionList">                           
            <c:forEach  var="DimensionStones" items="${updateStock.dimensionStones}" varStatus="i" begin="0">
              <tr class="dimensionStone">    
                 <td><form:input path="dimensionStones[${i.index}].stoneNo" id="stoneNo${i.index}" cssClass="controlStock"/></td>
                 <td><form:input path="dimensionStones[${i.index}].length" id="length${i.index}" onchange="findDimension(this.id)" cssClass="controlStock"/></td>
                 <td><form:input path="dimensionStones[${i.index}].breadth" id="breadth${i.index}" onchange="findDimension(this.id)" cssClass="controlStock"/></td>
                 <td><form:input path="dimensionStones[${i.index}].height" id="height${i.index}" onchange="findDimension(this.id)" cssClass="controlStock"/></td>
                 <td><form:input path="dimensionStones[${i.index}].dimension" id="dimension${i.index}" cssClass="controlStock"/></td>                 
                 <td><form:checkbox path="dimensionStones[${i.index}].isIssued" id="isIssued${i.index}" value="" cssClass="check"/></td>
                 <td><a href="#" class="removeDimensionStone"><img src="images/cross1.jpg" width="20" height="20" title="Remove Dimension Stone"/></a></td>

              </tr>
            </c:forEach>   
            <tr>

            </tr>             
            <c:if test="${empty updateStock.dimensionStones}">
                <tr class="dimensionStone defaultRow">    
                            <td><input type="text" name="dimensionStones[].stoneNo" value="" id="stoneNo" Class="controlStock"/></td>
                            <td><input type="text" name="dimensionStones[].length" value="" id="length" onchange="findDimension(this.id)" Class="controlStock"/></td>
                            <td><input type="text" name="dimensionStones[].breadth" value="" id="breadth" onchange="findDimension(this.id)" Class="controlStock"/></td>
                            <td><input type="text" name="dimensionStones[].height" value="" id="height" onchange="findDimension(this.id)" Class="controlStock"/></td>
                            <td><input type="text" name="dimensionStones[].dimension" value="" id="dimension" disabled="disabled" Class="controlStock"/></td>                            
                            <td><input type="checkbox" name="dimensionStones[].isIssued" value="Yes" id="isIssued" Class="controlStock"/></td> 
                            <td><a href="#" class="removeDimensionStone"><img src="images/cross1.jpg" width="20" height="20" title="Remove Dimension Stone"/></a></td>
                        </tr>
            </c:if>   
           </tbody>  
         </table>           
       </td>    
    </tr>    
  </table>
  <table  border="1" width="600"> 
   <tr>
      <td colspan="3" align="left"><a href="#" id="addDimensionStone"><img src="images/plus3.png" width="20" height="20" title="Add Dimension Stone"/></a></td>
   </tr> 
   <tr></tr> 
   <tr></tr> 
   <tr>
     <td colspan="3" align="center" width="200" height="180">
      <input type="submit" name="_target0" value="Submit" class="butn" />
     </td>
    </tr>  
  </table>  
</form:form>

This display the values in collection in command object and enable us to deleting, editing and adding new rows.

But my problem is,deleting does not clear the corresponding object in collection in command object.As a result threre is unwanted DIMENSIONSTONE objects.What i have to do ,to solve this problem?please advice me .......

标签: spring-mvc
1条回答
我想做一个坏孩纸
2楼-- · 2019-09-07 06:27

Removal of items is a problem with using Spring to bind to Collections. The problem is that the command object persists, and there is no way on the UI-side to tell the WebBinder to remove certain items out of the collection. Its can, in essence, handle only inserting things into the specified possitions given the index/key passed in from the View.

To get around this limitation, what I have done in the past is added a boolean called "removal" to my beans in the collection. Then, when I remove the object I simply replace the HTML DOM with a hidden field like:

<input type="hidden" name="beans[3].removal" value="true" />

Then, in my controller, I simply loop through the collection and remove the stuff that has the "removal" flag set.

Another alternative would be to store the total number in your command object. Then, when you get to the Controller, simply truncate the collection after that count.

EDIT: Example (from my head)

Command object:

public class FormBean {
  private List<MyBean> myBeans;
  // ...getters/setters etc
}

Bean in the collection in Command Object:

public class MyBean {
  private String someProperty;
  private Integer someOtherProperty;

  private boolean removal;
  // Getters/Setters
}

Form:

<!-- standard HTML stuff -->
<script>
  $(document).ready(function() {
    // Click handler to replace the contents of div.row with the removal flag
    $('.btnRemove').click(function() {
      var count = $(this).parent().prev('.row').length;
      $(this).parent().removeClass('rowActive').addClass('rowRemove');
      $(this).parent().hide();
      $(this).parent().html(function() {
        return $("<input/>",{
          id: 'myBeans'+count+'.removal',
          name: 'myBeans['+count+'].removal',
          value: 'true'
        });
      });
    });
  });
</script>
<!-- More HTML stuff -->
<form:form modelAttribute="formBean" action="/someUrl">
  <c:forEach items="${formBean.myBeans}" var="myBean" varStatus="status">
    <div class="row rowActive">
      <form:input path="myBeans[${status.index}].someProperty" />
      <input type="button" class="btnRemove" value="Remove" />
    </div>
  </c:forEach>
</form:form>

Then, somewhere in your controller code:

Set<MyBean> removals = new HashSet<MyBean>();
for(MyBean myBean : formBean.getMyBeans) {
  if(myBean.isRemoval) {
    removals.add(myBean);
  }
}
formBean.getMyBeans().removalAll(removals);

First: I typed this COMPLETELY from memory, directly into the post edit box. Milage may vary...

Second: I use jQuery. You may not. Replace any jQuery stuff with the appropriate not-jQuery stuff if you need to.

Third: There are some pieces that I glossed over. For instance, the reason I have a "row" class on the div is so I can get a total count for adding new rows, as you have to know which index to add. The "rowActive" and "rowRemove" classes on the div are used to get counts for other things, like for instance disabling remove buttons when there is only one item left. Other things I didn't go into is adding rows and setting up the event handlers (which you need to do every time you modify the DOM as you are adding new buttons that need event handlers)...I am not going to write the whole app for you, and I assume if you got this far you can figure these things out.

I have used this approach in about 6 different Spring MVC applications. The JavaScript is complex and it requires you to manually add and remove the elements. But this is the best way I have found to support the addition and removal of items from a collection using Spring MVC. In my case, cleanup on the Controller-side isn't a big issue because we do our own validation using a ValidationService object, so I just throw the cleanup routine in there. If there are other, better approaches I would LOVE to see them.

If you were using AJAX (which we are not, hence the reason I needed a solution that allowed a round-trip request to the server), you could simplify the process considerably by providing CRUD methods to handle adding/removing items in the collection.

Edit: Updating info

In the interest of maintaining this answer, I wanted to note that I have since switched tactics for the jQuery pieces of this. Instead of creating the DOM elements directly, I have grown fond of using .clone() and some creative regex to create the new elements and rename and re-id them. The benefit of doing this, IMHO, is that the code is greatly reduced as you can do a deep clone and get entire blocks of DOM at once. For example, if I had a table and I was trying to add entire rows to it, one might do something like:

function addTableRow() {
  var $lastActiveRow = $('#myTable tr.rowActive:last-child');  // Gets the last active row
  var newActiveRow = $lastActiveRow.clone();
  var count = $('#myTable tr.rowActive').length;

  // Fix the name and identifier of all inputs in the new row
  $(newActiveRow).find(':input').each(function() {
    var name = $(this).attr('name');
    var identifier = $(this).attr('id');
    $(this).attr('name',name.replace(/(\d+)/g,count));  // matches and replaces digits that look like "xxx[1]"
    $(this).attr('id',identifier.replace(/(\d+)/g,count));  // matches and replaces digits that look like "xxx1"
  });
  $('#myTable').append(newActiveRow);
}

So, in this example I first grab the last active row in the table, then I clone that row, then I loop through the inputs of that row and reset the name and id using regex and a count of all the active rows. Using my older method, the jQuery gets out of hand quickly trying to add highly complex DOM trees.

Some things to note: First, the jQuery clone() method is broken for IE 7 and lower do to problems in how those versions of IE. Also, the each() loop didn't reset the value of the inputs at all. The cloned inputs would have the same value of the ones you cloned from, so you would need to do that.

Finally, I'd like to point out that the clone() method by default does not clone event handlers, so any buttons and such that you have attached jQuery event handlers too will not work. You can pass in a boolean to instruct the jQuery clone() the events as well, but then you have the issue where the event is being handled by the wrong element (it doesn't clone the event handler, just the element but applies the old handler to the new element so you end up with crisscrossed handlers). The better solution is to use delegated events by attaching the handler at a higher level element (see Direct and delegated events section here).

查看更多
登录 后发表回答