Перейти к основному содержимому

Привязка списка в Thymeleaf

· 4 мин. чтения

1. Обзор

В этом кратком руководстве мы покажем, как связать объект List в Thymeleaf .

Чтобы узнать, как интегрировать Thymeleaf с Spring, вы можете ознакомиться с нашей основной статьей Spring здесь, где вы также можете узнать, как отображать поля, принимать ввод, отображать ошибки проверки или преобразовывать данные для отображения.

2. Списки в примере Thymeleaf

Мы начнем с демонстрации того, как отображать элементы списка на странице Thymeleaf и как связать список объектов в качестве входных данных пользователя в форме Thymeleaf .

Для этой цели мы будем использовать простую модель, показанную в следующем коде:

public class Book {
private long id;

private String title;

private String author;

// getters and setters
}

Помимо отображения существующих книг в нашем примере, мы дадим пользователю возможность добавлять несколько книг в коллекцию, а также редактировать все существующие книги одновременно.

3. Отображение элементов списка

Давайте взглянем на следующий метод контроллера , который возвращает страницу allBooks :

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

Здесь мы добавили объекты List of Book в качестве атрибута модели, отправляемого в представление, где мы будем отображать его с помощью таблицы 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>

Здесь мы используем свойство th:each для перебора списка и отображения свойств каждого объекта в нем.

4. Привязка списка с использованием выражения выбора

Чтобы отправить список объектов из представления в контроллер через отправку формы, мы не можем использовать сам объект List .

Вместо этого мы должны добавить объект-оболочку, который будет содержать отправленный список:

public class BooksCreationDto {
private List<Book> books;

// default and parameterized constructor

public void addBook(Book book) {
this.books.add(book);
}

// getter and setter
}

Теперь давайте позволим пользователю добавить три книги в одну отправку формы.

Во-первых, мы подготовим страницу формы, передав наш объект команды в качестве атрибута модели :

@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";
}

Как мы видим, мы передали в представление список из 3 пустых объектов Book через класс-оболочку.

Далее нам нужно добавить форму на страницу 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>

И вот как будет выглядеть страница выше:

./c12bcf3b7f55319f1a514cc12fb363aa.png

Давайте поближе посмотрим, что мы здесь сделали. Во- первых, мы использовали th:object="${form}" для указания объекта команды (тот, который мы передали как атрибут модели ).

Следующее, что стоит отметить, это то, что мы получили доступ к списку с помощью выражения выбора, используя:

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

И, наконец, мы сопоставляем наши входные данные как свойства элементов списка, используя th:field.

Однако нам также нужно использовать переменную itemStat , чтобы определить, на какой элемент списка мы ссылаемся, как показано в:

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

Последний шаг — фактически манипулировать отправленными данными на серверной части. Мы будем использовать объект команды как @ModelAttribute в нашем методе @PostMapping в контроллере, сохраним полученный список книг и вернем пользователю все существующие книги:

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

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

После отправки формы в конечную точку /save мы получим страницу со всеми недавно добавленными книгами:

./f853f078df2fddccf77ef7f6bf2c19be.png

5. Привязка списка с помощью выражения переменной

В этом примере мы сначала загрузим все существующие книги в объект команды:

@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";
}

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>

Как показано в <tr th:each="book, itemStat : ${form.books}"> , мы получили доступ к списку немного другим способом, на этот раз используя переменное выражение. Особенно уместно отметить, что мы указали имя и значение для элементов ввода, чтобы правильно отправлять данные .

Нам также пришлось добавить скрытый ввод, который будет связывать идентификатор текущей книги, потому что мы хотим не создавать новые книги, а редактировать существующие.

6. Заключение

В этой статье мы показали, как использовать объект List в Thymeleaf и Spring MVC. Мы показали, как отобразить список объектов, отправленных в представление, но сосредоточили основное внимание на двух способах привязки пользовательского ввода в виде списка в форме Thymeleaf.

Все фрагменты кода, упомянутые в статье, можно найти в нашем репозитории GitHub .