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>
И вот как будет выглядеть страница выше:
Давайте поближе посмотрим, что мы здесь сделали. Во- первых, мы использовали 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
мы получим страницу со всеми недавно добавленными книгами:
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 .