I want to edit exist data from database via html-form with field. But I can not create a right controller for it, because this code just created a new book. Old data was not changed.
Controller
#GetMapping("/bookUpdate/{id}")
public String bookListUpdate(#PathVariable (value = "id") Integer id, Model model, #Valid
BookDto book) {
model.addAttribute("book", service.findById(id));
return "views/bookUpdate";
}
#PostMapping("/edit")
public String editBook(#Valid Book book, #PathVariable (value = "id") Integer id) {
Book newBook = service.findById(id);
newBook.setDescription(book.getDescription());
newBook.setTopic(book.getTopic());
newBook.setLink(book.getLink());
service.saveBook(newBook);
return "views/index";
}
BookUpdate
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
layout:decorate="~{fragments/main_layout}">
<head>
<title>Book Form</title>
</head>
<body>
<div layout:fragment="content" class="container mySpace">
<form th:action="#{/edit}" th:object="${book}" method="post">
<div class="form-group">
<label for="topic" class="form-control-label">Topic</label> <input
type="text" class="form-control" th:field="*{topic}" id="topic" />
</div>
<div class="form-group">
<label for="description" class="form-control-label">Description</label>
<textarea class="form-control" th:field="*{description}"
id="description" style="height: 95px"></textarea>
</div>
<div class="form-group">
<label for="link" class="form-control-label">Link</label><a href=""> <input
type="text" class="form-control" th:field="*{link}" id="link" />
</div>
<input type="submit" value="Submit" class="btn btn-primary" />
</form>
</div>
</body>
</html>
Your #PostMapping is missing the path variable:
#PostMapping("/edit")
Do something like:
#PostMapping("/edit/{id}")
On a side note, you can make your URLs a bit nicer, by using something like #GetMapping("/books/{id}") and #PostMapping("/books/{id}").
Related
I want to edit data from database via form "bookUpdate.html", that I can open from page "bookList.html" with the list of all rows from the table. I created a controller and form, that redirect me to the page with filled fields, that contain right data according the value "id", where object = "book". Button "Edit" redirect me to the page "/bookUpdate/1?", it is right. But when I try to confirm a new information via button on this page, this action redirect me to "bookUpdate.html" with an error 404, and new data were not saved in database. I can not find a mistake in this case.
bookUpdate.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
layout:decorate="~{fragments/main_layout}">
<head>
<title>Book Form</title>
</head>
<body>
<div layout:fragment="content" class="container mySpace">
<form th:action="#{/bookUpdate}" th:object="${book}" method="post">
<div class="form-group">
<label for="topic" class="form-control-label">Topic</label> <input
type="text" class="form-control" th:value="${book.topic}"
id="topic" />
</div>
<div class="form-group">
<label for="description" class="form-control-label">Description</label>
<textarea class="form-control" th:value="${book.description}"
id="description" style="height: 95px"></textarea>
</div>
<div class="form-group">
<label for="link" class="form-control-label">Link</label> <input
type="text" class="form-control" th:value="${book.link}" id="link" />
</div>
<input type="submit" value="Submit" class="btn btn-primary" />
</form>
</div>
</body>
</html>
Contoller
#Controller
public class BookUpdateController {
#Autowired
private BookService service;
#PostMapping("/bookUpdate/{id}")
public String editBook(#Valid BookDto book, #PathVariable (value = "id") Integer id, Model
model) {
Book oldBook = service.findById(id);
oldBook.setDescription(book.getDescription());
oldBook.setTopic(book.getTopic());
oldBook.setLink(book.getLink());
service.saveBook(oldBook);
model.addAttribute("book", new BookDto());
return "redirect:/bookList";
}
}
#GetMapping("/bookUpdate/{id}")
public String bookListUpdate(#PathVariable (value = "id") Integer id, Model model) {
model.addAttribute("book", service.findById(id));
return "views/bookUpdate";
}
bookList.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
layout:decorate="~{fragments/main_layout}">
<head>
<title>Books</title>
</head>
<body>
<div layout:fragment="content" class="container mySpace">
<form action="/bookList" class="form-inline">
<div class="form-group mb-2" >
<input type="text" class="form-control" name="name"
placeholder="Search book" /> <input type="submit" value="Search"
class="btn btn-primary" />
</div>
</form>
<div class="card">
<div class="card card-body">
<ul th:each="books:${book}" style="list-style: none; padding-left: 10px;">
<li><b>Topic:</b> <span th:text="${books.topic}"></span></li>
<li><b>Description:</b><p th:text="${books.description}"></p></li>
<li><b>Link:</b> <span th:text="${books.link}"></span></li>
<br>
<form class="form-inline my-2 my-lg-0"
th:action="#{'/bookUpdate/'+${books.id}}" th:object="${books}" method="get">
<input class="form-control mr-sm-2" type="hidden" />
<button class="btn btn-secondary my-2 my-sm-0" type="submit"
>Edit</button>
</form>
<hr>
</ul>
</div>
</div>
</div>
</body>
</html>
In your bookUpdate.html file, the action property is not set correctly. Your form after posting send information to #{/bookUpdate} but in related controller, defined PostMapping required #PostMapping("/bookUpdate/{id}") and they are mismach.
Change your form action property to :
<form th:action="#{'/bookUpdate/'+${book.id}}" th:object="${book}" method="post">
A have simple CRUD web application. And I want to bind UI and backend with thymeleaf. After I create some data and get server response - all styles are disappearing. I'm new to thymeleaf, CSS and HTML. Can someone help me to figure out where is the problem?
Before and after:
Save operation method:
#PostMapping("/user/save")
public ModelAndView save(#ModelAttribute("userDTO") #Valid UserDTO userDTO,
BindingResult bindingResult, WebRequest request, Errors errors) {
User registered = new User();
if (!bindingResult.hasErrors()) {
registered = createUserAccount(userDTO, bindingResult);
}
if (registered == null) {
bindingResult.rejectValue("email", "message.regError");
}
if (bindingResult.hasErrors()) {
bindingResult.getAllErrors().forEach(error -> log.error(error.toString()));
return new ModelAndView("authorization/registration", "error", bindingResult.getAllErrors());
} else {
return new ModelAndView("users", "user", userDTO);
}
}
registration.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>User Registration</title>
<div th:replace="fragments/css_fragments :: css_background_layer"></div>
</head>
<body>
<div style="text-align:center">
<div th:replace="fragments/menu_fragments :: header_menu"></div>
</div>
<div style="margin: 0 auto; width: 20%; padding-top: 18%;">
<div class="registration-form">
<!--/*#thymesVar id="userDTO" type="com.socnetw.socnetw.model.UserDTO"*/-->
<form id="form" method="post" action="/user/save" th:object="${userDTO}">
<label>
<input name="username" placeholder="Username" required="required" th:field="*{username}"
type="text">
</label>
<ul>
<li th:each="err : ${#fields.errors('username')}" th:text="${err}"></li>
</ul>
<label>
<input name="realName" placeholder="Real Name"
type="text" th:field="*{realName}">
</label>
<ul>
<li th:each="err : ${#fields.errors('realName')}" th:text="${err}"></li>
</ul>
<span></span><br>
<label>
<input name="email" placeholder="Email" required="required" th:field="*{email}"
type="email">
</label>
<ul>
<li th:each="err : ${#fields.errors('email')}" th:text="${err}"></li>
</ul>
<label>
<input name="phoneNumber" placeholder="Phone Number" required="required" th:field="*{phoneNumber}"
type="tel">
</label>
<ul>
<li th:each="err : ${#fields.errors('phoneNumber')}" th:text="${err}"></li>
</ul>
<span></span><br>
<label>
<input name="password" placeholder="Password" th:field="*{password}"
required="required" type="password">
</label>
<ul>
<li th:each="err : ${#fields.errors('password')}" th:text="${err}"></li>
</ul>
<label>
<input name="passwordMatcher" placeholder="Repeat password" th:field="*{matchingPassword}"
required="required" type="password">
</label>
<ul>
<li th:each="err : ${#fields.errors('matchingPassword')}" th:text="${err}"></li>
</ul>
<span></span><br>
<button type="submit" style="margin-top: 20px">Register</button>
</form>
</div>
</div>
</body>
</html>
css fragment
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/html">
<div th:fragment="css_background_layer">
<link href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css"
rel="stylesheet"
th:href="#{'https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css'}"
type="text/css">
<link href="css/style.css" rel="stylesheet"
th:href="#{css/style.css}"
type="text/css">
<div class="overlay"></div>
</div>
</html>
You need to use an absolute url to your css, rather than a relative one. When you go to /user/save it's looking for /user/save/css/style.css -- which probably doesn't exist.
th:href="#{/css/style.css}"
On my view, I generate input "username", select with attribute multiple=multiple, which has name "rolesss".
My issue is, that if I send such form via post, my controller should convert roles to list, but i get only list containing single element.
For example:
I send post, with values:
username:MyUser
_csrf:aef50238-92cf-48df-86a4-cb6e2b8f62c9
rolesss:USER
rolesss:ADMIN
In debug mode in my controller I see values:
roless: "USER"
username: "MyUser"
"ADMIN" did just disappear.
My controller looks like:
#Controller
#RequestMapping("/user-management")
public class UserManagementController {
#RequestMapping("")
public ModelAndView home() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("pages/user-management");
return modelAndView;
}
#RequestMapping(value = "", method = RequestMethod.POST)
public ModelAndView changeRoles(#ModelAttribute("username") String username,#ModelAttribute("rolesss") List<String> rolesss) {
return null;
}
}
My view I merged 2 thymeleaf fragments into 1, in my code #user-roles-form is in separate fragment, but I think that it should not change anything:
<th:block layout:fragment="main-content">
<section>
<h2 class="section-title no-margin-top">Role Management</h2>
<div class="form-group">
<div class="panel panel-primary">
<div class="panel-heading">Změna rolí uživatele</div>
<div class="panel-body">
<form class="form-horizontal" role="form" action="/user-management" method="post">
<div class="form-group">
<label for="user-name" class="col-sm-2 control-label">Uživatel</label>
<div class="col-sm-10">
<input id="user-name" class="autocomplete form-control" type="text"
placeholder="Jméno uživatele" name="username"/>
</div>
<input type="hidden"
th:name="${_csrf.parameterName}"
th:value="${_csrf.token}"/>
</div>
<div id="user-roles-form" th:fragment="roles(roles)" xmlns:th="http://www.thymeleaf.org">
<div class="form-group">
<label for="user-roles" class="col-sm-2 control-label">Uživatelovy role</label>
<div class="col-sm-10">
<select multiple="multiple" class="form-control" id="user-roles" name="rolesss">
<th:block th:each="role : ${roles}">
<option th:value="${role.userRole.role}" th:text="${role.userRole.role}" th:selected="${role.selected}"></option>
</th:block>
</select>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-ar btn-primary">Uložit</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</section>
</th:block>
try using like below in your controller
in your HTML or JSP
<form modelAttribute="your_model_name"></form>
if you are using model attribute then use #ModelAttribute
otherwise, use #RequestParam("username")
In your case
#RequestMapping(value = "", method = RequestMethod.POST)
public ModelAndView changeRoles(#RequestParam("username") String username,#RequestParam("rolesss") List<String> rolesss) {
..........
.........
return null;
}
I need to post data from html form into database. I have gradle project with springboot and thymeleaf.
I have java object Class witch has id, name, description, teacher and minutes.
In html I use modal form to ask for all but id.
main.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Class Scheduler</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" type="text/css" href="webjars/bootstrap/4.0.0-beta.2/css/bootstrap.min.css"/>
<script type="text/javascript" src="webjars/jquery/3.2.1/jquery.min.js"></script>
<script type="text/javascript" src="webjars/bootstrap/4.0.0-beta.2/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="container">
<h2>Class Scheduler</h2>
<div class="text-right">
<button type="button" class="btn btn-success" data-toggle="modal" data-target="#addClassModal">Add new class</button>
</div>
<div class="modal fade" id="addClassModal" tabindex="-1" role="dialog" aria-labelledby="addClassModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addClassModalLabel">Add new class</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form th:action="#{/addClass}" th:object="${class}" method="post">
<div class="form-group">
<label for="name">Name:</label>
<input type="text" class="form-control" id="name" th:value="*{name}"/>
</div>
<div class="form-group">
<label for="description">Description:</label>
<input type="text" class="form-control" id="description" th:value="*{description}"/>
</div>
<div class="form-group">
<label for="teacher">Teacher:</label>
<input type="text" class="form-control" id="teacher" th:value="*{teacherName}"/>
</div>
<div class="form-group">
<label for="minutes">Minutes:</label>
<input type="number" class="form-control" id="minutes" th:value="*{timeMinutes}"/>
</div>
<input type="submit" value="Submit" />
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
And my controller looks like this:
#Controller
public class ScheduleController {
#Autowired
private ClassService classService;
#Autowired
private StudentService studentService;
#RequestMapping(value = "/addClass", method = RequestMethod.POST)
public String addClass(#RequestAttribute("class") Class newClass, Model model) {
classService.addClass(newClass);
return "main";
}
The error message, that I get is:
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'class' available as request attribute.
What am I doing wrong?
Edit:
My ClassService looks like this:
#Service
public class ClassService{
private static final BeanPropertyRowMapper<Class> CLASS_ROW_MAPPER = BeanPropertyRowMapper.newInstance( Class.class );
#Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void addClass(final Class newClass) {
MapSqlParameterSource in = new MapSqlParameterSource();
in.addValue("id", newClass.getId());
in.addValue("name", newClass.getName());
in.addValue("description", newClass.getDescription());
in.addValue("teacher_name", newClass.getTeacherName());
in.addValue("time_minutes", newClass.getTimeMinutes());
namedParameterJdbcTemplate.update("INSERT INTO class(name, description, teacher_name, time_minutes) values(:name,:description,:teacher_name,:time_minutes)", in);
}
Here
public String addClass(#RequestAttribute("class") Class newClass, Model model) {
Use ModelAttribute instead of RequestAttribute
public String addClass( #ModelAttribute("class") Class newClass, Model model) {
Also, in your controller class return an instance of Class associated with the ModelAttribute
#ModelAttribute(value = "class")
public Class getClass()
{
return new Class();
}
A question regarding Apache Wicket: I have a form which contains several text input fields. It works fine until I add a DateTextField. The overriden method onSubmit() is simply not called anymore. I looked at the Wicket examples but cannot see a major difference to my code.
Here's the html code:
<html xmlns:wicket="http://wicket.apache.org">
<head>
<title>Students</title>
</head>
<body>
<div class="container">
<form id="createStudent" wicket:id="createStudent">
<div>
<span id="lastNameLabel"><wicket:message key="lastNameLabel" /></span>
<input id="lastName" wicket:id="lastName" type="text" />
</div>
<div>
<span id="firstNameLabel"><wicket:message key="firstNameLabel" /></span>
<input id="firstName" wicket:id="firstName" type="text" />
</div>
<div>
<span id="dateOfBirthLabel"><wicket:message key="dateOfBirthLabel" /></span>
<input id="dateOfBirth" wicket:id="dateOfBirth" type="text" />
</div>
<div>
<input id="submit" type="submit" value="" wicket:message="value:submitLabel"/>
</div>
</form>
</div>
</body>
</html>
The corresponding java file:
package it.foo;
import java.util.Locale;
import org.apache.wicket.Session;
import org.apache.wicket.datetime.StyleDateConverter;
import org.apache.wicket.datetime.markup.html.form.DateTextField;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.joda.time.DateTime;
public class CreateStudentForm extends Form<Student> {
private static final long serialVersionUID = 1L;
private String lastName;
private String firstName;
private DateTime dateOfBirth;
#SpringBean
StudentDao studentDao;
public CreateStudentForm(String id) {
super(id);
setDefaultModel(new CompoundPropertyModel<>(this));
add(new TextField<String>("lastName"));
add(new TextField<String>("firstName"));
final DateTextField dateOfBirthField = new DateTextField("dateOfBirth", new StyleDateConverter("S-", true)) {
private static final long serialVersionUID = 1L;
#Override
public Locale getLocale() {
return Session.get().getLocale();
}
};
dateOfBirthField.setType(DateTime.class);
add(dateOfBirthField);
}
#Override
protected void onSubmit() {
// does not get called as soon as the DateTextField is present. Works fine otherwise.
Student student = new Student(this.lastName, this.firstName, this.dateOfBirth);
studentDao.store(student);
setResponsePage(StudentsPage.class);
}
}
I had a look at the html which is rendered in the browser. If the dateTextField is NOT present, it looks like this:
<html>
<head>
<title>Students</title>
</head>
<body>
<div class="container">
<form id="createStudent" method="post" action="./?2-1.IFormSubmitListener-createStudent"><div style="width:0px;height:0px;position:absolute;left:-100px;top:-100px;overflow:hidden"><input type="hidden" name="createStudent_hf_0" id="createStudent_hf_0" /></div>
<div>
<span id="lastNameLabel">Nachname: </span>
<input id="lastName" type="text" value="" name="lastName"/>
</div>
<div>
<span id="firstNameLabel">Vorname:</span>
<input id="firstName" type="text" value="" name="firstName"/>
</div>
<!-- <div>
<span id="dateOfBirthLabel"><wicket:message key="dateOfBirthLabel" /></span>
<input id="dateOfBirth" wicket:id="dateOfBirth" type="text" />
</div> -->
<div>
<input id="submit" type="submit" value="Schüler anlegen"/>
</div>
</form>
</div>
</body>
</html>
As soon as the dateTextField is present, the form has additional divisions with JavaScript calls.
<html>
<head>
<title>Students</title>
</head>
<body>
<div class="container">
<form id="createStudent" method="post" action="./?6-7.IFormSubmitListener-createStudent"><div style="width:0px;height:0px;position:absolute;left:-100px;top:-100px;overflow:hidden"><input type="hidden" name="createStudent_hf_0" id="createStudent_hf_0" /></div><div style="width:0px;height:0px;position:absolute;left:-100px;top:-100px;overflow:hidden"><input type="text" tabindex="-1" autocomplete="off"/><input type="submit" tabindex="-1" name="p::submit" onclick=" var b=document.getElementById('submit'); if (b!=null&&b.onclick!=null&&typeof(b.onclick) != 'undefined') { var r = Wicket.bind(b.onclick, b)(); if (r != false) b.click(); } else { b.click(); }; return false;" /></div>
<div>
<span id="lastNameLabel">Nachname: </span>
<input id="lastName" type="text" value="Matthias" name="lastName"/>
</div>
<div>
<span id="firstNameLabel">Vorname:</span>
<input id="firstName" type="text" value="Tonhäuser" name="firstName"/>
</div>
<div>
<span id="dateOfBirthLabel">Geburtsdatum: </span>
<input id="dateOfBirth" type="text" value="06.09.17" name="dateOfBirth"/>
</div>
<div>
<input id="submit" type="submit" value="Schüler anlegen" name="p::submit"/>
</div>
</form>
</div>
</body>
</html>
I don't quite understand why the additional divisions are there all off a sudden. My guess is that the JavaScript call does not work.
Thanks.
It is likely that the onSubmit() method does not call may be because of incorrect validation of dateField's value. You have to add feedback panel to you page and check .
You can override the onError(...) method and look whats going on..