В этой статье продолжим разбирать приложение из предыдущего примера. На этот раз сосредоточимся на синтаксисе шаблонов Thymeleaf. В пример будет добавлена проверка поля и вывод в шаблон ошибок.
Maven-зависимость
Чтобы добавить Thymeleaf в Spring Boot приложение, мы прописали в POM-файле зависимость:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
Отображение атрибутов модели
На главной странице мы добавляли в модель два атрибута: animals и animal:
Set<Animal> animals = animalCart.getAnimals(); model.addAttribute("animals", animals); model.addAttribute("animal", new Animal());
animals — коллекция, animal объект.
В шаблоне атрибуты становятся переменными, к которым есть доступ.
Вообще наша страница выглядит так:

В шаблоне выводится коллекция animals. А перед ней находится форма, которая через заполненный animal отправляет введенное название животного в контроллер.
th:each — отображение коллекции
Начнем с коллекции animals. Мы ее выводим в виде таблицы:
<table> <thead> <tr> <th>ID</th> <th>Животное</th> </tr> </thead> <tbody> <tr th:each="an, iterStat : ${animals}" th:style="${iterStat.odd}? 'font-weight: bold;'"> <td th:text="${iterStat.index}+1"></td> <td th:text="${an.name}"></td> </tr> </tbody> </table>
${animals} — это обращение к переменной, содержащей коллекцию. Строка <tr> будет повторяться столько раз, сколько элементов в коллекции, для этого мы задаем для нее:
<tr th:each="an, iterStat : ${animals}"
Здесь an — текущее животное, iterStat — итератор.
Из него можно получить индекс:
${iterStat.index}
а также, является ли индекс нечетным числом:
${iterStat.odd}
th:style — задание стиля
Если является, ты мы задаем строке стиль:
th:style="${iterStat.odd}? 'font-weight: bold;'"
Так все нечетные строки будут выделены жирным шрифтом.
th:text — отображение текста внутри тега
Как вывести имя животного? Для этого есть очень популярный атрибут th:text:
<td th:text="${an.name}"></td>
Он заполняет тег значением из переменной.
Если название животного — «козел», то вышеприведенная запись преобразуется на итоговой странице в:
<td>козел</td>
Перейдем к форме.
Обработка формы: th:action, th:object, th:field
Форма у нас обрабатывается благодаря трем атрибутам:
- th:action=»@{/}»
- th:object=»${animal}»
- th:field=»*{name}»
где первый атрибут th:action задает адрес /, по которому идет POST-запрос с формы, второй — объект Animal, а третий — его поле name, которое заполняется и передается в контроллер. Обратите внимание, что переменная ${animal} задана в th:object, поэтому имя поля прописывается со звездочки.
<form th:action="@{/}" th:object="${animal}" th:method="post"> <div> <label>Название: </label> <input type="text" th:field="*{name}"></input> </div> <div> <button type="submit">Добавить в корзину</button> </div> </form>
Форма преобразуется в итоге:
<form action="/" method="post"> <div> <label>Название:</label> <input type="text" id="name" name="name" value=""></input> </div> <div> <button type="submit">Добавить в корзину</button> </div> </form>
th:value
Но можно обойтись без th:object, в этом случае тегу <input> прописываем атрибут th:value:
<input type="text" th:value="${animal.name}" name="name"/>
Разницы нет, в обоих случаях в теле POST-запроса уходит name=значение, а итоговый <input>:
<input type="text" value="" name="name"/>
Проверка поля и вывод ошибок — th:errors
Добавим в код проверку корректности введенного значения имени и вывод ошибок. Для этого придется поменять код в нескольких местах.
Модель — @NotBlank и @Size(min=2, max=50)
Во-первых, добавим для поля name модели условие, при котором оно считается корректным:
@NoArgsConstructor @Data @Entity public class Animal { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private long id; @NotBlank @Size(min=2, max=50) private String name; }
То есть поле name должно быть не пустым (не состоять из пробелов) и иметь размер от 2 до 50.
Чтобы аннотации @NotBlank и@Size(min=2, max=50) компилировались и обрабатывались, необходимо добавить Maven-зависимость:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
@Valid и BindingResult в контроллере
Во-вторых, исправим контроллер.
Значение name должно проверяться, когда Animal приходит в контроллер в метод add().
Чтобы это происходило, аннотируем animal с помощью @Valid, а следующим аргументом добавим BindingResult (порядок важен):
@PostMapping("/") public String add(@Valid Animal animal, BindingResult bindingResult, Model model) { if (bindingResult.hasErrors()) { model.addAttribute("animals", animalCart.getAnimals()); return "index"; } animalCart.add(animal); return "redirect:/"; }
Теперь перенаправление («redirect:/») на get() делаем только в случае успеха, иначе же просто возвращаем index.html с ошибками. Модель тоже надо заполнять, чтобы не терять список уже добавленных животных.
th:errors — вывод ошибок
После всех приготовлений вывести ошибки просто. Просто добавим под <input> тег:
<div th:errors="*{name}" class="error"/>
В итоге при попытке добавить пустую строку получим:

Итоги
Код примера доступен на GitHub. В нем добавлена проверка поля и вывод ошибок, касающихся этого поля.