我在将表单回发到控制器时遇到很多困难,该表单应该仅包含用户可以编辑的对象的数组列表。
表单可以正确加载,但是在发布时,似乎从未实际发布过任何内容。
这是我的表格:
<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方法:
th:field
... 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()等等,所以它的东西应该有一个数组,但是,我不能在传递数组。
get()
add()
clearAll()
我也尝试过使用th:field=${},这样会导致运行时异常
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);)
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标头中的发布回。
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方法:
@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该项目在列表中的位置在哪里:
input
name
value
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
null
clientList[1] = 'B' clientList[3] = 'D'
结果ArrayList将是:[null, B, null, D]
ArrayList
[null, B, null, D]
更新1:
在上面的示例中,ClientForm是的包装List<String>。但在你的情况下ClientWithSelectionListWrapper包含ArrayList<ClientWithSelection>。因此,clientList[1]应该clientList[1].clientID与要发送回的其他属性依此类推:
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>
我已经构建了一个小演示,因此你可以对其进行测试:
应用程序
@SpringBootApplication public 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
@Controller class 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>