Binding a List in Thymeleaf – 绑定百里叶的清单

最后修改: 2018年 6月 12日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this quick tutorial, we’re going to show how to bind a List object in Thymeleaf.

在这个快速教程中,我们将展示如何在Thymeleaf中绑定一个List对象

To learn how to integrate Thymeleaf with Spring, you can check out our main Spring article here – where you can also learn how to display fields, accept input, display validation errors, or convert data for display.

要了解如何将Thymeleaf与Spring集成,您可以查看我们的主要Spring文章–在这里您还可以了解如何显示字段、接受输入、显示验证错误或转换数据以便显示。

2. Lists in Thymeleaf Example

2.Thymeleaf中的列表示例

We’ll start by showing how to display elements of a List in a Thymeleaf page and how to bind a list of objects as user’s inputs in a Thymeleaf form.

我们将首先展示如何在Thymeleaf页面中显示List中的元素,以及如何在Thymeleaf表单中绑定列表对象作为用户的输入

For this purpose, we’ll use a simple model shown in the following code:

为此,我们将使用以下代码所示的一个简单模型。

public class Book {
    private long id;

    private String title;

    private String author;
	
    // getters and setters
}

Beside displaying existing books in our example, we’re going to make it possible for the user to add multiple books to the collection and also to edit all existing books at once.

在我们的例子中,除了显示现有的书之外,我们将使用户有可能向集合中添加多本书,也可以一次性编辑所有现有的书。

3. Displaying List Elements

3.显示列表元素

Let’s take a look at the following Controller method that returns the allBooks page:

让我们看一下下面的Controller方法,它返回allBooks页面。

@GetMapping("/all")
public String showAll(Model model) {
    model.addAttribute("books", bookService.findAll());
    return "books/allBooks";
}

Here, we’ve added List of Book objects as a model attribute sent to the view, where we’ll display it using an HTML table:

在这里,我们添加了ListBook对象作为模型属性发送到视图,在那里我们将使用HTML表格来显示它。

<table>
    <thead>
        <tr>
            <th> Title </th>
            <th> Author </th>
        </tr>
    </thead>
    <tbody>
	<tr th:if="${books.empty}">
            <td colspan="2"> No Books Available </td>
        </tr>
        <tr th:each="book : ${books}">
            <td><span th:text="${book.title}"> Title </span></td>
            <td><span th:text="${book.author}"> Author </span></td>
        </tr>
    </tbody>
</table>

Here, we’re using the th:each property to iterate through the list and display properties of each object in it.

这里,我们使用th:each属性来迭代列表,并显示其中每个对象的属性。

4. Binding a List Using Selection Expression

4.使用选择表达式绑定一个列表

To send the list of objects from the view to the controller via form submit, we cannot use List object itself.

为了通过表单提交将对象的列表从视图发送到控制器,我们不能使用List对象本身。

Instead, we have to add a wrapper object that will hold the submitted list:

相反,我们必须添加一个封装对象,该对象将容纳提交的列表:

public class BooksCreationDto {
    private List<Book> books;

    // default and parameterized constructor

    public void addBook(Book book) {
        this.books.add(book);
    }
	
    // getter and setter
}

Let’s now enable the user to add three books in one form submission.

现在让我们让用户在一次表单提交中添加三本书。

First, we’ll prepare the form page, passing our command object as a Model attribute:

首先,我们要准备好表单页面,将我们的命令对象作为Model属性传递。

@GetMapping("/create")
public String showCreateForm(Model model) {
    BooksCreationDto booksForm = new BooksCreationDto();

    for (int i = 1; i <= 3; i++) {
        booksForm.addBook(new Book());
    }

    model.addAttribute("form", booksForm);
    return "books/createBooksForm";
}

As we can see, we passed a list of 3 empty Book objects to the view via the wrapper class.

我们可以看到,我们通过包装类将3个空的Book对象列表传递给了视图。

Next, we need to add the form to a Thymeleaf page:

接下来,我们需要将该表单添加到Thymeleaf页面中。

<form action="#" th:action="@{/books/save}" th:object="${form}"
  method="post">
    <fieldset>
        <input type="submit" id="submitButton" th:value="Save">
        <input type="reset" id="resetButton" name="reset" th:value="Reset"/>
        <table>
            <thead>
                <tr>
                    <th> Title</th>
                    <th> Author</th>
                </tr>
            </thead>
            <tbody>
                <tr th:each="book, itemStat : *{books}">
                    <td><input th:field="*{books[__${itemStat.index}__].title}" /></td>
                    <td><input th:field="*{books[__${itemStat.index}__].author}" /></td>
                </tr>
            </tbody>
        </table>
    </fieldset>
</form>

And this is what the page above will look like:

而这就是上面的页面的样子。

books2018/06/allbooks.png

Let’s have a closer look at what we did here. First, we used the th:object=”${form}” to specify the command object (the one we passed as a Model attribute).

让我们仔细看看我们在这里做了什么。首先,我们用th:object=”${form}”来指定命令对象(就是我们作为Model属性传递的那个)。

The next thing worth noting is that we accessed the list with a selection expression using:

接下来值得注意的是,我们用一个选择表达式访问了列表,使用的是。

<tr th:each="book, itemStat : *{books}">

And finally, we’re mapping our inputs as properties of the list elements using th:field.

最后,我们使用th:field.将我们的输入映射为列表元素的属性。

However, we also need to use the itemStat variable to define which list element we’re referring to, as demonstrated in:

然而,我们还需要使用itemStat变量来定义我们所指的是哪个列表元素,如图所示。

th:field="*{books[__${itemStat.index}__].title}"

The last step is actually to manipulate the submitted data on the back-end. We’ll use the command object as the @ModelAttribute in our @PostMapping method in the controller, save the retrieved list of books and return all existing books to the user:

最后一步实际上是在后端操作提交的数据。我们将在控制器的@PostMapping方法中使用命令对象作为@ModelAttribute,保存检索到的书籍列表,并将所有现有书籍返回给用户。

@PostMapping("/save")
public String saveBooks(@ModelAttribute BooksCreationDto form, Model model) {
    bookService.saveAll(form.getBooks());

    model.addAttribute("books", bookService.findAll());
    return "redirect:/books/all";
}

After submitting the form to the /save endpoint, we’ll get the page with all the newly added books:

在向/save端点提交表单后,我们会得到包含所有新添加书籍的页面。

allbooks

5. Binding a List Using Variable Expression

5.使用变量表达式绑定一个列表

For this example, we’ll first load all existing books into the command object:

在这个例子中,我们首先将所有现有的书加载到命令对象中。

@GetMapping("/edit")
public String showEditForm(Model model) {
    List<Book> books = new ArrayList<>();
    bookService.findAll().iterator().forEachRemaining(books::add);

    model.addAttribute("form", new BooksCreationDto(books));
    return "books/editBooksForm";
}

The HTML page is similar, with the most noteworthy differences in the th:each block:

HTML页面是类似的,最值得注意的是th:each块中的差异。

<tr th:each="book, itemStat : ${form.books}">
    <td>
        <input hidden th:name="|books[${itemStat.index}].id|" th:value="${book.getId()}"/>
    </td>
    <td>
        <input th:name="|books[${itemStat.index}].title|" th:value="${book.getTitle()}"/>
    </td>
    <td>
        <input th:name="|books[${itemStat.index}].author|" th:value="${book.getAuthor()}"/>
    </td>
</tr>

As shown in <tr th:each=”book, itemStat : ${form.books}”>, we accessed the list in a slightly different manner, using variable expression this time. Especially relevant is to notice that we provided name and value for input elements to properly submit data.

<tr th:each=”book, itemStat : ${form.books}”>所示,我们访问列表的方式略有不同,这次使用了变量表达。特别相关的是,注意到我们为输入元素提供了namevalue,以正确提交数据

We also had to add hidden input which will bind the current book’s id because we don’t want to create new books but to edit existing ones.

我们还必须添加隐藏的输入,它将绑定当前书籍的ID,因为我们不想创建新的书籍,而是要编辑现有的书籍。

6. Conclusion

6.结论

In this article, we illustrated how to use List object in Thymeleaf and Spring MVC. We’ve shown how to display the list of objects sent to the view, but we put the primary focus on two ways of binding user inputs as a list in Thymeleaf form.

在本文中,我们说明了如何在Thymeleaf和Spring MVC中使用List对象。我们展示了如何显示发送到视图的对象的列表,但我们把主要重点放在两种在Thymeleaf表单中把用户输入绑定为列表的方式上。

All of the code snippets, mentioned in the article, can be found in our GitHub repository.

文章中提到的所有代码片段,都可以在我们的GitHub仓库中找到。