如何将对象列表与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>

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

应用程序

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

以上是 如何将对象列表与thymeleaf绑定? 的全部内容, 来源链接: utcz.com/qa/429614.html

回到顶部