【发布时间】:2016-07-29 18:48:56
【问题描述】:
我在将表单回传到控制器时遇到了很多困难,控制器应该只包含一个用户可以编辑的对象的数组列表。
表单加载正确,但在发布时,它似乎从未真正发布任何内容。
这是我的表格:
<form action="#" th:action="@{/query/submitQuery}" th:object="${clientList}" method="post">
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>Select</th>
<th>Client ID</th>
<th>IP Addresss</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr th:each="currentClient, stat : ${clientList}">
<td><input type="checkbox" th:checked="${currentClient.selected}" /></td>
<td th:text="${currentClient.getClientID()}" ></td>
<td th:text="${currentClient.getIpAddress()}"></td>
<td th:text="${currentClient.getDescription()}" ></td>
</tr>
</tbody>
</table>
<button type="submit" value="submit" class="btn btn-success">Submit</button>
</form>
以上工作正常,它可以正确加载列表。但是,当我发布时,它返回一个空对象(大小为 0)。我相信这是由于缺少th:field,但无论如何这里是控制器POST方法:
...
private List<ClientWithSelection> allClientsWithSelection = new ArrayList<ClientWithSelection>();
//GET method
...
model.addAttribute("clientList", allClientsWithSelection)
....
//POST method
@RequestMapping(value="/submitQuery", method = RequestMethod.POST)
public String processQuery(@ModelAttribute(value="clientList") ArrayList clientList, Model model){
//clientList== 0 in size
...
}
我尝试添加th:field,但无论我做什么,它都会导致异常。
我试过了:
...
<tr th:each="currentClient, stat : ${clientList}">
<td><input type="checkbox" th:checked="${currentClient.selected}" th:field="*{}" /></td>
<td th th:field="*{currentClient.selected}" ></td>
...
我无法访问 currentClient(编译错误),我什至无法选择 clientList,它为我提供了 get()、add()、clearAll() 等选项,所以它应该有一个数组,但是,我不能传入数组。
我也尝试过使用th:field=${} 之类的东西,这会导致运行时异常
我试过了
th:field = "*{clientList[__currentClient.clientID__]}"
但也编译错误。
有什么想法吗?
更新 1:
Tobias 建议我需要将我的列表包装在一个包装器中。所以这就是我所做的:
ClientWithSelectionWrapper:
public class ClientWithSelectionListWrapper {
private ArrayList<ClientWithSelection> clientList;
public List<ClientWithSelection> getClientList(){
return clientList;
}
public void setClientList(ArrayList<ClientWithSelection> clients){
this.clientList = clients;
}
}
我的页面:
<form action="#" th:action="@{/query/submitQuery}" th:object="${wrapper}" method="post">
....
<tr th:each="currentClient, stat : ${wrapper.clientList}">
<td th:text="${stat}"></td>
<td>
<input type="checkbox"
th:name="|clientList[${stat.index}]|"
th:value="${currentClient.getClientID()}"
th:checked="${currentClient.selected}" />
</td>
<td th:text="${currentClient.getClientID()}" ></td>
<td th:text="${currentClient.getIpAddress()}"></td>
<td th:text="${currentClient.getDescription()}" ></td>
</tr>
然后是我的控制器:
@RequestMapping(value="/submitQuery", method = RequestMethod.POST)
public String processQuery(@ModelAttribute ClientWithSelectionListWrapper wrapper, Model model){
...
}
页面加载正确,数据按预期显示。如果我在没有任何选择的情况下发布表单,我会得到:
org.springframework.expression.spel.SpelEvaluationException: EL1007E:(pos 0): Property or field 'clientList' cannot be found on null
不知道为什么抱怨
(在 GET 方法中它有:model.addAttribute("wrapper", wrapper);)
如果我随后进行选择,即勾选第一个条目:
There was an unexpected error (type=Bad Request, status=400).
Validation failed for object='clientWithSelectionListWrapper'. Error count: 1
我猜我的 POST 控制器没有得到 clientWithSelectionListWrapper。不知道为什么,因为我已将包装器对象设置为通过 FORM 标头中的 th:object="wrapper" 发回。
更新 2:
我已经取得了一些进展!最后,提交的表单被控制器中的 POST 方法拾取。但是,所有属性似乎都为空,除了该项目是否已被勾选。我进行了各种更改,这就是它的外观:
<form action="#" th:action="@{/query/submitQuery}" th:object="${wrapper}" method="post">
....
<tr th:each="currentClient, stat : ${clientList}">
<td th:text="${stat}"></td>
<td>
<input type="checkbox"
th:name="|clientList[${stat.index}]|"
th:value="${currentClient.getClientID()}"
th:checked="${currentClient.selected}"
th:field="*{clientList[__${stat.index}__].selected}">
</td>
<td th:text="${currentClient.getClientID()}"
th:field="*{clientList[__${stat.index}__].clientID}"
th:value="${currentClient.getClientID()}"
></td>
<td th:text="${currentClient.getIpAddress()}"
th:field="*{clientList[__${stat.index}__].ipAddress}"
th:value="${currentClient.getIpAddress()}"
></td>
<td th:text="${currentClient.getDescription()}"
th:field="*{clientList[__${stat.index}__].description}"
th:value="${currentClient.getDescription()}"
></td>
</tr>
我还在包装类中添加了一个默认的无参数构造函数,并向 POST 方法添加了一个bindingResult 参数(不确定是否需要)。
public String processQuery(@ModelAttribute ClientWithSelectionListWrapper wrapper, BindingResult bindingResult, Model model)
当然,systemInfo 应该是 null(在这个阶段),但是 clientID 总是 0,而 ipAddress/Description 总是 null。尽管对于所有属性,选定的布尔值都是正确的。我确定我在某处的其中一个属性上犯了一个错误。回到调查。
更新 3:
好的,我已经成功地正确填写了所有值!但是我不得不更改我的td 以包含一个<input />,这不是我想要的......尽管如此,这些值正在正确填充,这表明spring 可能会寻找一个输入标签来进行数据映射?
这是我如何更改 clientID 表数据的示例:
<td>
<input type="text" readonly="readonly"
th:name="|clientList[${stat.index}]|"
th:value="${currentClient.getClientID()}"
th:field="*{clientList[__${stat.index}__].clientID}"
/>
</td>
现在我需要弄清楚如何将其显示为纯数据,理想情况下不存在任何输入框...
【问题讨论】:
-
绑定仅适用于
input元素,客户端回传到服务器。其他框架可以使用某种视图状态或会话并向开发人员隐藏详细信息,但 AFAIK timeleaf 不这样做。在这种特殊情况下,您可以将值绑定到隐藏字段。 -
@user1516873 是的,您是对的,实际上是在您发表评论前 30 秒发现的。是的,一定是 thymeleafff 相关的东西,我很确定当我在 asp.net 中做类似的事情时,它直接把它捡起来了。无论如何,我一定会写下来作为提醒!
标签: java spring spring-mvc spring-boot thymeleaf