#thymeleaf
#thymeleaf
Вопрос:
Я могу сохранить книгу и автора в базе данных. При редактировании содержимого записи (название книги, жанр и автор) я теряю часть автора. На странице редактирования список авторов отображается с помощью выпадающего списка. Идея в том, что должен быть выбран конкретный автор, пока пользователь не изменил его.
вот контроллер и шаблон для книги.
@RequestMapping(path = {"/edit", "/edit/{id}"})
public String editBookById(Model model, @PathVariable("id")
Optional<Long>id)
throws NotFoundException
{
if (id.isPresent()) {
Optional<Book> response = bookRepository.findById(id.get());
if(!response.isPresent()){
throw new NotFoundException("Book not found.");
// return "not-found";
}
Book book = response.get();
model.addAttribute("book", book);
} else {
model.addAttribute("book", new Book());
}
model.addAttribute("authors", authorRepository.findAll());
return "books/add-edit";
}
Шаблон:
<form action="@/books/edit" th:object="${book}" method='POST'>
<table>
<tr>
<td>Select Authors:</td>
<td>
<select id="authorsList" name="authors" field="*{author}"><!---1,2,3,4,5--><!---the secret-->
<!--<option value="Author">Author</option>-->
<option th:each="author :${authors}"
th:value="${author.firstName}"
th:text="${author.firstName} ' ' ${author.id}"
th:selected="${author}">
</option>
</select>
</td>
</tr>
</table>
</form>
Ответ №1:
Если вы хотите реализовать редактирование формы, вам необходимо реализовать GetMapping
и a PostMapping
(оба являются мета-аннотациями RequestMapping
).
Когда браузер сначала показывает форму, он выполняет GET. Метод в контроллере для поддержки этого должен быть чем-то вроде:
@GetMapping("/edit/{id}")
public String showEditForm(Model model, @PathVariable("id") Long id) {
Book book = bookRepository.findById(id).orElseThrow( ()-> new NotFoundException("Book not found. Id: " id));
model.addAttribute("book", book);
model.addAttribute("authors", authorRepository.findAll());
return "books/add-edit";
}
При отправке формы происходит публикация, поэтому вам нужен @PostMapping
. Что-то вроде:
@PostMapping("/edit/{id}")
public String doEditBook(@PathVariable("id") Long id, @Valid @ModelAttribute("book") Book book, BindingResults bindingResults, Model model) {
if(bindingResults.hasErrors()) {
model.addAttribute("authors", authorRepository.findAll());
return "books/add-edit"
}
// save book updates in db here
bookService.updateBook(book);
return "redirect:/books";
}
Однако, как правило, использовать вашу Book
сущность непосредственно в форме — плохая идея. Лучше создать DTO, что-то вроде EditBookFormData
, которое напрямую сопоставляется с полями формы:
public class EditBookFormData {
private Long id; // this is the id of the book itself
private Long authorId;
// other fields here
public static EditBookFormData fromBook(Book book) {
// create a new EditBookFormData instance here and populate the fields
}
}
Обновленный GetMapping
становится:
@GetMapping("/edit/{id}")
public String showEditForm(Model model, @PathVariable("id") Long id) {
Book book = bookRepository.findById(id).orElseThrow( ()-> new NotFoundException("Book not found. Id: " id));
model.addAttribute("book", EditBookFormData.fromBook(book));
model.addAttribute("authors", authorRepository.findAll());
return "books/add-edit";
}
Сообщение становится:
@PostMapping("/edit/{id}")
public String doEditBook(@PathVariable("id") Long id, @Valid @ModelAttribute("book") EditBookFormData formData, BindingResults bindingResults, Model model) {
if(bindingResults.hasErrors()) {
model.addAttribute("authors", authorRepository.findAll());
return "books/add-edit"
}
// save book updates in db here
bookService.updateBook(formData);
return "redirect:/books";
}
И шаблон:
<form action="@/books/edit" th:object="${book}" method='POST'>
<table>
<tr>
<td>Select Authors:</td>
<td>
<select id="authorsList" name="authors" th:field="*{authorId}">
<option th:each="author :${authors}"
th:value="${author.id}"
th:text="${author.firstName} ' ' ${author.lastName}">
</option>
</select>
</td>
</tr>
</table>
</form>