Spring绑定动态列表成员

最近遇到一个需求,使用Spring MVC 3,需要绑定动态列表成员。Google了好多文章,没有找到完美的解决方案,绑定到不成问题,主要是动态添加、删除导致绑定的列表下标,在提交之前需要进行normalize,使用js进行normalize可行,但是费劲而且如果有对表单新的修改导致已有的js出现错误,本文提供了一个简单可行的易于维护的方法:

在添加的时候,我们根据当前条目的数量,计算新添加条目的下标。
在删除的时候,使用js将删除的条目隐藏,并将删除列表的下标记录到隐藏字段中。
这样在后台,我们只需要将删除的条目,按照记录在隐藏字段的下标删除,然后再保存即可。

关于绑定动态列表,在Spring 2.5中,不能使用普通的List,必须使用AutoPopulatingList进行绑定,否则会导致OutOfBoundException,Spring 3提供了Auto Grown的功能,但还仅限于List和Array,map和set还不能Auto Grown。
下面以一个简单的订单修改的例子说明如何进行动态列表绑定:
一个订单,有很多订单条目,用户可以修改订单,动态删除或者添加订单条目。
先看看model:
订单类,一个订单有一个订单列表:

package com.qunar.advertisement.advertiser.model;

import java.util.List;
/**
* Author: fuliang
* http://fuliang.iteye.com
*/
public class Order {
private Long id;
private String name;
private List<OrderItem> orderItems;

public void setId(Long id) {
this.id = id;
}

public Long getId() {
return id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public void setOrderItems(List<OrderItem> orderItems) {
this.orderItems = orderItems;
}

public List<OrderItem> getOrderItems() {
return orderItems;
}
}

订单条目类:

package com.qunar.advertisement.advertiser.model;
/**
* Author: fuliang
* http://fuliang.iteye.com
*/
public class OrderItem {
private Long id;
private String product;
private Integer price;

public void setId(Long id) {
this.id = id;
}

public Long getId() {
return id;
}

public Integer getPrice() {
return price;
}

public void setPrice(Integer price) {
this.price = price;
}

public void setProduct(String product) {
this.product = product;
}

public String getProduct() {
return product;
}
}

我们的控制器使用@ModelAttribute注解和表单绑定:

package com.qunar.advertisement.advertiser.controller;

import java.util.Collections;
import java.util.List;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import com.qunar.advertisement.advertiser.model.Order;
import com.qunar.advertisement.advertiser.service.OrderManger;
/**
* Author: fuliang
* http://fuliang.iteye.com
*/
@Controller
@RequestMapping("/orders")
public class OrderController {
private OrderManger orderManager;

@Autowired
public void setOrderManger(OrderManger orderManager){
this.orderManager = orderManager;
}


@RequestMapping("/edit/{id}")
public ModelAndView edit(@PathVariable("id") Long id){
return new ModelAndView("/orders/edit_order",Collections.singletonMap("order", orderManager.getOrderById(id)));
}

@RequestMapping("/update")
public ModelAndView update(@ModelAttribute("order") @Valid Order order,@RequestParam(value="deletedIndexes",required=false) List<Integer> deletedIndexes){
orderManager.update(order,deletedIndexes);
return new ModelAndView("/orders/home");
}
}


edit_order.jsp的表单和js添加、删除:

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jstl/fn" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<jsp:include page="../base.jsp"></jsp:include>
<head>
<script src="js/jquery-1.3.1.js" type="text/javascript"></script>
<title>Order Home</title>

<script type="text/javascript">
function deleteItem(itemIndex){
$('#item_' + itemIndex).hide();
$('#deletedIndexes').append("<input type='hidden' name='deletedIndexes' value='" + itemIndex + "'");
}

function addItem(){
var itemCnt = parseInt($('#itemCnt').val());

var newItem =
'<tr id="item_' + itemCnt + '">' +
'<th>订单条目' + (itemCnt + 1)+ '</th>' +
'<td><input type="hidden" name="orderItems[' + itemCnt + '].id"/>' +
'<input type="text" name="orderItems[' + itemCnt + '].product"/>' +
'</td>' +
'<td><input type="text" name="orderItems[' + itemCnt + '].price"/></td>' +
'<td><a href="javascript:void(0)" onclick="deleteItem('+ itemCnt + ')">删除条目</a></td>' +
'</tr>';

$('#item_' + (itemCnt-1)).after(newItem);
$('#itemCnt').val(itemCnt + 1);
}
</script>
</head>
<body>
<div id="form">
<form:form commandName="order" id="orderForm" action="orders/update" method="post">
<form:hidden path="id"/>
<div id="deletedIndexes" style="display:none;">

</div>
<input type="hidden" value="${fn:length(order.orderItems)}" id="itemCnt"/>
<table>
<tr>
<th>订单名</th>
<td colspan="3"><form:input path="name"/></td>
</tr>
<c:forEach varStatus="vs" items="${order.orderItems}">
<tr id="item_${vs.index}">
<th>订单条目${vs.index + 1}</th>
<td><form:hidden path="orderItems[${vs.index}].id"/>
<form:input path="orderItems[${vs.index}].product"/>
</td>
<td><form:input path="orderItems[${vs.index}].price"/></td>
<td><a href="javascript:void(0)" onclick="deleteItem('${vs.index}')">删除条目</a></td>
</tr>
</c:forEach>
<tr>
<td colspan="4" style="text-align:center">
<input type="submit" value="保存"/>
<input type="button" value="添加订单条目" onclick="addItem()"/>
</td>
</tr>
</table>
</form:form>
</div>
</body>
</html>

在Serice类中我们只是简单的准备数据和打印出结果查看效果:

package com.qunar.advertisement.advertiser.service;

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

import org.springframework.stereotype.Service;

import com.qunar.advertisement.advertiser.model.Order;
import com.qunar.advertisement.advertiser.model.OrderItem;
/**
* Author: fuliang
* http://fuliang.iteye.com
*/
@Service
public class OrderManger {
//Simple data for test
public Order getOrderById(Long id){
Order order = new Order();
order.setId(id);
order.setName("手机订单");
List<OrderItem> orderItems = new ArrayList<OrderItem>();
order.setOrderItems(orderItems);

OrderItem orderItem1 = new OrderItem();
orderItem1.setId(1L);
orderItem1.setProduct("iPhone 4");
orderItem1.setPrice(5000);
orderItems.add(orderItem1);

OrderItem orderItem2 = new OrderItem();
orderItem2.setId(2L);
orderItem2.setProduct("Nokia N97");
orderItem2.setPrice(4000);
orderItems.add(orderItem2);

return order;
}
/**
*
* @param order should be updated
* @param deletedItemIndexes orderItem indexes should be deleted
* 简单的在控制台打印出结果,如果存入到数据库,可以根据orderItem id
* 是否为null,进行更新或者保存。
*/
public void update(Order order, List<Integer> deletedItemIndexes) {
if(deletedItemIndexes != null){
for(int i = deletedItemIndexes.size() - 1; i >= 0; i--){
order.getOrderItems().remove(deletedItemIndexes.get(i).intValue());
}
}
StringBuilder sb = new StringBuilder();
sb.append("Order Id: ").append(order.getId()).append("\tName: ").append(order.getName()).append("\n");
List<OrderItem> items = order.getOrderItems();
for (OrderItem orderItem : items) {
sb.append("Item Id: ").append(orderItem.getId()).append("\tName: ").append(orderItem.getProduct()).append("\tprice: ").append(orderItem.getPrice()).append("\n");
}
System.out.println(sb.toString());
}
}


BTW:Java的自动拆装箱要注意,特别是List#remove有两个重载的方法,
remove(Object) remove(int),这个必须手工拆箱。
【评估多目标跟踪方法】9个高度敏捷目标在编队中的轨迹和测量研究(Matlab代码实现)内容概要:本文围绕“评估多目标跟踪方法”,重点研究9个高度敏捷目标在编队飞行中的轨迹生成与测量过程,并提供完整的Matlab代码实现。文中详细模拟了目标的动态行为、运动约束及编队结构,通过仿真获取目标的状态信息与观测数据,用于验证和比较不同多目标跟踪算法的性能。研究内容涵盖轨迹建模、噪声处理、传感器测量模拟以及数据可视化等关键技术环节,旨在为雷达、无人机编队、自动驾驶等领域的多目标跟踪系统提供可复现的测试基准。; 适合人群:具备一定Matlab编程基础,从事控制工程、自动化、航空航天、智能交通或人工智能等相关领域的研究生、科研人员及工程技术人员。; 使用场景及目标:①用于多目标跟踪算法(如卡尔曼滤波、粒子滤波、GM-CPHD等)的性能评估与对比实验;②作为无人机编队、空中交通监控等应用场景下的轨迹仿真与传感器数据分析的教学与研究平台;③支持对高度机动目标在复杂编队下的可观测性与跟踪精度进行深入分析。; 阅读建议:建议读者结合提供的Matlab代码进行实践操作,重点关注轨迹生成逻辑与测量模型构建部分,可通过修改目标数量、运动参数或噪声水平来拓展实验场景,进一步提升对多目标跟踪系统设计与评估的理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值