如何将对象列表与thymeleaf绑定?
我在将表单回发到控制器时遇到很多困难,该表单应该仅包含用户可以编辑的对象的数组列表。
表单可以正确加载,但是在发布时,似乎从未实际发布过任何内容。
这是我的表格:
<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>
上面的工作正常,它可以正确加载列表。但是,当我POST时,它返回一个空对象(大小为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(编译错误),我甚至不能选择客户端列表,它给了我类似的选项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。不知道为什么,因为我将包装对象设置为通过th:object="wrapper"FORM
标头中的发布回。
更新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>
我还向包装器类添加了默认的无参数构造函数,bindingResult并向POST方法添加了参数(不确定是否需要)。
public String processQuery(@ModelAttribute ClientWithSelectionListWrapper wrapper, BindingResult bindingResult, Model model)
因此,在发布对象时,其外观如下:
当然,应该假定systemInfo为空(在此阶段),但是clientID始终为0,而ipAddress / Description始终为null。尽管对于所有属性,所选的布尔值都是正确的。我确定我对某处某个属性犯了一个错误。回到调查。
更新3:
好的,我设法正确填写了所有值!但是我必须更改我的名称,td以包括不是我想要的内容…但是,这些值正确填充,这表明spring寻找的也许是数据映射的输入标签?
这是我如何更改clientID表数据的示例:
<td> <input type="text" readonly="readonly"
th:name="|clientList[${stat.index}]|"
th:value="${currentClient.getClientID()}"
th:field="*{clientList[__${stat.index}__].clientID}"
/>
</td>
现在我需要弄清楚如何将其显示为纯数据,理想情况下不存在任何输入框…
回答:
你需要一个包装对象来保存提交的数据,如下所示:
public class ClientForm { private ArrayList<String> clientList;
public ArrayList<String> getClientList() {
return clientList;
}
public void setClientList(ArrayList<String> clientList) {
this.clientList = clientList;
}
}
并将其用作@ModelAttribute
你的processQuery
方法:
@RequestMapping(value="/submitQuery", method = RequestMethod.POST)public String processQuery(@ModelAttribute ClientForm form, Model model){
System.out.println(form.getClientList());
}
此外,该input
元素需要a name
和a value
。如果你直接构建html,请考虑到名称必须为clientList[i]
,i
该项目在列表中的位置在哪里:
<tr th:each="currentClient, stat : ${clientList}"> <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>
注意,clientList
可以包含null
在中间位置。例如,如果发布的数据为:
clientList[1] = 'B'clientList[3] = 'D'
结果ArrayList
将是:[null, B, null, D]
更新1:
在上面的示例中,ClientForm
是的包装List<String>
。但在你的情况下ClientWithSelectionListWrapper
包含ArrayList<ClientWithSelection>
。因此,clientList[1]
应该clientList[1].clientID
与要发送回的其他属性依此类推:
<tr th:each="currentClient, stat : ${wrapper.clientList}"> <td><input type="checkbox" th:name="|clientList[${stat.index}].clientID|"
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>
我已经构建了一个小演示,因此你可以对其进行测试:
应用程序
@SpringBootApplicationpublic class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
ClientWithSelection.java
public class ClientWithSelection { private Boolean selected;
private String clientID;
private String ipAddress;
private String description;
public ClientWithSelection() {
}
public ClientWithSelection(Boolean selected, String clientID, String ipAddress, String description) {
super();
this.selected = selected;
this.clientID = clientID;
this.ipAddress = ipAddress;
this.description = description;
}
/* Getters and setters ... */
}
ClientWithSelectionListWrapper.java
public class ClientWithSelectionListWrapper {
private ArrayList<ClientWithSelection> clientList;
public ArrayList<ClientWithSelection> getClientList() {
return clientList;
}
public void setClientList(ArrayList<ClientWithSelection> clients) {
this.clientList = clients;
}
}
TestController.java
@Controllerclass TestController {
private ArrayList<ClientWithSelection> allClientsWithSelection = new ArrayList<ClientWithSelection>();
public TestController() {
/* Dummy data */
allClientsWithSelection.add(new ClientWithSelection(false, "1", "192.168.0.10", "Client A"));
allClientsWithSelection.add(new ClientWithSelection(false, "2", "192.168.0.11", "Client B"));
allClientsWithSelection.add(new ClientWithSelection(false, "3", "192.168.0.12", "Client C"));
allClientsWithSelection.add(new ClientWithSelection(false, "4", "192.168.0.13", "Client D"));
}
@RequestMapping("/")
String index(Model model) {
ClientWithSelectionListWrapper wrapper = new ClientWithSelectionListWrapper();
wrapper.setClientList(allClientsWithSelection);
model.addAttribute("wrapper", wrapper);
return "test";
}
@RequestMapping(value = "/query/submitQuery", method = RequestMethod.POST)
public String processQuery(@ModelAttribute ClientWithSelectionListWrapper wrapper, Model model) {
System.out.println(wrapper.getClientList() != null ? wrapper.getClientList().size() : "null list");
System.out.println("--");
model.addAttribute("wrapper", wrapper);
return "test";
}
}
test.html
<!DOCTYPE html><html>
<head></head>
<body>
<form action="#" th:action="@{/query/submitQuery}" th:object="${wrapper}" 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 : ${wrapper.clientList}">
<td><input type="checkbox" th:name="|clientList[${stat.index}].clientID|"
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>
</tbody>
</table>
<button type="submit" value="submit" class="btn btn-success">Submit</button>
</form>
</body>
</html>
更新1.B:
以下是使用th:field
并发送所有其他属性作为隐藏值的相同示例。
<tbody> <tr th:each="currentClient, stat : *{clientList}">
<td>
<input type="checkbox" th:field="*{clientList[__${stat.index}__].selected}" />
<input type="hidden" th:field="*{clientList[__${stat.index}__].clientID}" />
<input type="hidden" th:field="*{clientList[__${stat.index}__].ipAddress}" />
<input type="hidden" th:field="*{clientList[__${stat.index}__].description}" />
</td>
<td th:text="${currentClient.getClientID()}"></td>
<td th:text="${currentClient.getIpAddress()}"></td>
<td th:text="${currentClient.getDescription()}"></td>
</tr>
</tbody>
以上是 如何将对象列表与thymeleaf绑定? 的全部内容, 来源链接: utcz.com/qa/429614.html