gpt4 book ai didi

java - 如何将对象列表与 thymeleaf 绑定(bind)?

转载 作者:IT老高 更新时间:2023-10-28 13:04:31 26 4
gpt4 key购买 nike

我在将表单回传到 Controller 时遇到了很多困难, Controller 应该只包含一个用户可以编辑的对象的数组列表。

表单加载正确,但在发布时,它似乎从未真正发布任何内容。

这是我的表格:

<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 ,但无论如何这里是 Controller 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>

以上加载正常: enter image description here

然后是我的 Controller :

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

enter image description here

如果我随后进行选择,即勾选第一个条目:

There was an unexpected error (type=Bad Request, status=400).
Validation failed for object='clientWithSelectionListWrapper'. Error count: 1

我猜我的 POST Controller 没有得到 clientWithSelectionListWrapper。不知道为什么,因为我已将包装器对象设置为通过 th:object="wrapper" 回发。在 FORM 标题中。


更新 2:

我已经取得了一些进展!最后,提交的表单被 Controller 中的 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)

所以当一个对象被发布时,它的外观是这样的: enter image description here

当然,systemInfo 应该是 null(在这个阶段),但是 clientID 总是 0,而 ipAddress/Description 总是 null。尽管对于所有属性,选定的 boolean 值都是正确的。我确定我在某处的其中一个属性上犯了一个错误。回到调查。


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

现在我需要弄清楚如何将其显示为纯数据,理想情况下不存在任何输入框...

最佳答案

您需要一个包装器对象来保存提交的数据,如下所示:

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元素需要 namevalue .如果你直接构建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:

在我上面的例子中,ClientFormList<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>

我已经构建了一个小演示,因此您可以对其进行测试:

Application.java

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

关于java - 如何将对象列表与 thymeleaf 绑定(bind)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36500731/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com