在Spring MVC中编辑包含集合对象的Command对象

时间:2013-03-08 13:06:16

标签: spring-mvc

我正在使用spring MVC.My命令对象包含一个集合对象,它看起来如下

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;
}

}

此控制器类扩展了 AbstractWizardFormController

我使用 formBackingObject() 填充命令对象将其返回到表单

表格如下所示

  <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>

这将在命令对象中显示集合中的值,并使我们能够删除,编辑和添加新行。

但我的问题是,删除不清除命令对象集合中的相应对象。结果是不需要的DIMENSIONSTONE对象。我要做什么,解决这个问题?请指教我......

1 个答案:

答案 0 :(得分:0)

使用Spring绑定到集合时删除项目是一个问题。问题是命令对象仍然存在,并且UI端无法告诉WebBinder从集合中删除某些项目。从本质上讲,它可以只处理从View中传入的索引/键的指定插入内容。

为了解决这个限制,我过去所做的就是在集合中添加一个名为“remove”的布尔值给我的bean。然后,当我删除对象时,我只需将HTML DOM替换为隐藏字段,如:

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

然后,在我的控制器中,我只是遍历集合并删除设置了“删除”标记的内容。

另一种方法是将总数存储在命令对象中。然后,当您到达Controller时,只需在该计数后截断该集合。

编辑:示例(来自我的头脑)

命令对象:

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

Command对象集合中的Bean:

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

  private boolean removal;
  // Getters/Setters
}

形式:

<!-- 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>

然后,在控制器代码的某处:

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

首先:我从内存中直接输入此内容,直接进入帖子编辑框。 Milage可能会有所不同......

第二:我使用jQuery。你不可以。如果需要,用适当的not-jQuery替换任何jQuery内容。

第三:我掩饰了一些碎片。例如,我在div上有一个“行”类的原因是我可以获得添加新行的总计数,因为你必须知道要添加哪个索引。 div上的“rowActive”和“rowRemove”类用于获取其他内容的计数,例如,当只剩下一个项目时禁用删除按钮。我没有涉及的其他事情是添加行和设置事件处理程序(每次修改DOM时都需要这样做,因为你要添加需要事件处理程序的新按钮)...我不打算写整个应用程序适合你,我假设如果你到目前为止,你可以解决这些问题。

我在大约6种不同的Spring MVC应用程序中使用过这种方法。 JavaScript很复杂,需要您手动添加和删除元素。但这是我发现支持使用Spring MVC从集合中添加和删除项目的最佳方法。在我的情况下,Controller端的清理不是一个大问题,因为我们使用ValidationService对象进行自己的验证,所以我只是在那里抛出清理例程。如果还有其他更好的方法,我很乐意看到它们。

如果您使用的是AJAX(我们不是,因此我需要一个允许向服务器发送往返请求的解决方案),您可以通过提供CRUD方法来处理添加/删除项目来大大简化过程集合。

修改:更新信息

为了保持这个答案,我想要注意到我已经改变了jQuery这个问题的策略。我没有直接创建DOM元素,而是喜欢使用.clone()和一些创造性的正则表达式来创建新元素并重命名和重新识别它们。恕我直言,这样做的好处是代码大大减少了,因为你可以进行深度克隆并立即获得整个DOM块。例如,如果我有一个表,并且我试图将整行添加到其中,可能会执行以下操作:

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);
}

所以,在这个例子中,我首先获取表中的最后一个活动行,然后我克隆该行,然后循环遍历该行的输入并使用正则表达式重置名称和id以及所有活动行的计数。使用我的旧方法,jQuery很快失控,试图添加高度复杂的DOM树。

需要注意的一些事项:首先,对于IE 7,jQuery clone()方法是破坏的,而对于那些IE版本的问题则更低。而且,each()循环根本没有重置输入的值。克隆的输入将与您克隆的输入具有相同的值,因此您需要这样做。

最后,我想指出默认情况下clone()方法不会克隆事件处理程序,因此任何按钮以及附加了jQuery事件处理程序的按钮都不起作用。您可以传入一个布尔值来指示jQuery clone()事件,但是你遇到了错误元素处理事件的问题(它不会克隆事件处理程序,只是元素但是将旧处理程序应用于新元素,以便最终得到交叉处理程序。更好的解决方案是通过将处理程序附加到更高级别的元素来使用委托事件(请参阅直接和委派事件部分here)。