I have a project Spring Boot with Thymeleaf as front-end, I need to create an object from a form in view, this Object is complex:
#Entity
#Table(name = "tasks", catalog = "explorerrh")
public class Tasks implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private Integer idtasks;
private Employee employee;
private String taskName;
private String taskDescrption;
private Date taskTime;
private String statut;
As you see the bean Tasks has an attribute name Employee.
Now I want to create a form to create a "Task"
So in controller I pass the bean Tasks as an empty bean and bean Employee like this:
#RequestMapping(value = "/dashboard.html", method = RequestMethod.POST)
public ModelAndView loginUser(#ModelAttribute Login login, Model model) {
Employee employee = employeeService.
getEmployeeByMail(login.getMailAddress());
ModelAndView mav = new ModelAndView("dashboard");
mav.addObject("employee", employee);
mav.addObject("date", mediumDateFormat.format(date));
mav.addObject("task", new Tasks());
return mav;
}
Then:
In view Thymeleaf :
<form role="form" action="/addTask" th:action="#{/addTask}" th:object="${task}" method="POST">
<div class="form-group">
<label for="taskTitle"><span class="glyphicon glyphicon-user"></span> Task Title</label>
<input type = "text" class = "form-control" th:field="*{taskName}" id = "taskTitle" placeholder = "Enter Task title"/>
</div>
<div class="form-group">
<label for="taskTitle"><span class="glyphicon glyphicon-user"></span>employee</label>
<input type="text" class="form-control"
th:field="*{employee}" th:value="${employee}" id="employeeTask"/>
</div>
<div class="form-group">
<label for="taskDescription">
<span class="glyphicon glyphicon-eye-open"></span> Task Description</label>
<textarea name="taskDescription" th:field="*{taskDescrption}" class="form-control" rows="5" id="taskDescription"></textarea>
</div>
<div class="well">
<div id="datetimepicker1" class="input-append date">
<input data-format="dd/MM/yyyy hh:mm:ss"
name="taskDateTime" th:field="*{taskTime}" type="text"/>
<span class="add-on">
<i data-time-icon="icon-time" data-date-icon="icon-calendar">
</i>
</span>
</div>
</div>
<div class="form-group">
<label for="taskStatut"><span class="glyphicon glyphicon-user"></span> Task statut</label>
<input type = "text" class = "form-control" th:field="*{taskStatut}" id = "taskStatut" placeholder = "Enter Task statut"/>
</div>
<button type="submit" class="btn btn-success"><span class="glyphicon glyphicon-off"></span> add task</button>
</form>
Finally I implement a controller to intercept this form:
public ModelAndView addTask(#ModelAttribute Tasks tasks) {
ModelAndView mav = new ModelAndView("dashboard");
Employee employee = employeeService.
getEmployeeByIdemployee(String.valueOf(
tasks.getEmployee().getIdemployee()));
tasks.setStatut("Open");
tasks.setEmployee(employee);
tasksService.addTask(tasks);
return mav;
}
I have this error when sending the form:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Wed Apr 13 23:47:19 GMT+01:00 2016
There was an unexpected error (type=Bad Request, status=400).
Validation failed for object='tasks'. Error count: 2
The error said you that object can't be saved due to validation errors. Also it said that you have 2 validation errors.
In your form th:object named task, so rename it in submit method. Also you need to add BindingResult after bean to handle errors :
public ModelAndView addTask(#ModelAttribute("task") Tasks tasks, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
//errors handling
}
}
Related
I'm trying to implement a login form in a Spring boot application. It has an email and a password field. The email field failed to get user input, here is the form:
<form th:action="#{/login}" method="get" th:object="${loginForm}" style="max-width: 600px; margin: 0 auto;">
<div class="m-3">
<div class="form-group row">
<label class="col-4 col-form-label">E-mail: </label>
<div class="col-8">
<input type="text" th:field="*{email}" name="q" class="form-control" required />
</div>
</div>
<div class="form-group row">
<label class="col-4 col-form-label">Password: </label>
<div class="col-8">
<input type="password" th:field="*{password}" class="form-control" required/>
</div>
</div>
<div>
<button type="submit" class="btn btn-primary">Log in</button>
</div>
</div>
</form>
Here is the controller:
#GetMapping("login")
public ModelAndView login(Model model, #RequestParam(name = "q", required = false) Optional<String> email) {
Optional<UserDto> aUser;
System.out.println(email);
if (email.isPresent()) {
aUser = userService.getAUserByEmail(email.get());
model.addAttribute("user", aUser);
var mv = new ModelAndView("user/user-list", model.asMap());
return mv;
} else {
model.addAttribute("loginForm", new LoginForm());
return new ModelAndView("/login/login-form", model.asMap());
}
}
I thought the #RequestParam(name = "q") and name="q" in html would do the job, but I always get Optional.empty for email. Any idea what's wrong here?
UPDATE:
From the answers I changed controller to this:
#GetMapping("login")
public ModelAndView login(Model model, LoginForm loginForm) {
Optional<UserDto> aUser;
if (loginForm.getEmail() != null) {
aUser = userService.getAUserByEmail(loginForm.getEmail());
model.addAttribute("user", aUser);
var mv = new ModelAndView("user/user-list", model.asMap());
return mv;
} else {
model.addAttribute("loginForm", new LoginForm());
return new ModelAndView("/login/login-form", model.asMap());
}
}
login-form.html to this:
<form th:action="#{/login}" method="get" th:object="${loginForm}" style="max-width: 600px; margin: 0 auto;">
<div class="m-3">
<div class="form-group row">
<label class="col-4 col-form-label">E-mail: </label>
<div class="col-8">
<input type="text" th:field="*{email}" class="form-control" required />
</div>
</div>
<div class="form-group row">
<label class="col-4 col-form-label">Password: </label>
<div class="col-8">
<input type="password" th:field="*{password}" class="form-control" required/>
</div>
</div>
<div>
<button type="submit" class="btn btn-primary">Log in</button>
</div>
</div>
</form>
I also have LoginForm.java like this
#Data
#AllArgsConstructor
#NoArgsConstructor
public class LoginForm {
private String email;
private String password;
}
but still not getting user email field input?
The way you have set up your form, it's mapping the value of your email input field to the property email (that's what th:field="*{email}" means) of an object called loginForm (that's what th:object="${loginForm}" means). Neither of these seem to be used or even exist in your login() method. You need to either change what you use in your controller to match what you have in your Thymeleaf template, or change your Thymeleaf template to actually reference what you are using in your controller.
The problem in your code is located under th:object="${loginForm}"
With this you inform spring to bind the data sent from the form into an object named loginForm.
So Spring actually expects the controller to be
#GetMapping("login")
public ModelAndView login(Model model, LoginForm loginForm) {
....
and inside LoginForm a field named email will contain the value sent from the form, as you have declared with <input type="text" th:field="*{email}" .... >
If you don't want the data to be bound into an object from Spring Mvc then
remove the th:object="${loginForm}"
use the
<input type="text" th:name="q" class="form-control" required />
and then the controller will receive the sent value as a query parameter
#GetMapping("login")
public ModelAndView login(Model model, #RequestParam(name =
"q", required = false) Optional<String> email) {
I familiarize myself with Thymeleaf.
I am trying to get data from a POST-mapping in the controller.
But I got null variables when I call employee.getName().
I looked a lot online but can't find a solution.
My controller class
#GetMapping
public String getEmployees(Model model) {
model.addAttribute("newEmployee", new Employee());
return "employees";
}
#PostMapping
public String addEmployee(#ModelAttribute("newEmployee") Employee employee) {
System.out.println(employee); // return object
System.out.println(employee.getFirstName()); // return null
return "employees";
}
HTML form with Thymeleaf
<div class="modal-body">
<form th:action="#{/employees}" th:object="${newEmployee}" method="post">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label class="col-form-label">First Name <span class="text-danger">*</span></label>
<input th:field="*{firstName}" class="form-control" type="text">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label class="col-form-label">Last Name</label>
<input th:field="*{lastName}" class="form-control" type="text">
</div>
</div>
See tutorials:
Wim Deblauwe: Form handling with Thymeleaf
Baeldung: Binding a List in Thymeleaf
#GetMapping("/all")
public String showAll(Model model) {
model.addAttribute("employees", employeeService.findAll());
return "employees/all";
}
#GetMapping("/add")
public String createForm(Model model) {
model.addAttribute("newEmployee", new Employee()); // the form-data object as blank employee to be filled
return "employees/form"; // the page containing the form
}
#PostMapping
public String addEmployee(#ModelAttribute("newEmployee") Employee employee) {
System.out.println(employee); // return object
System.out.println(employee.getFirstName()); // return null if either not set in HTML-form or not mapped from HTML-form to POST correctly
// save new employee or add to a repository
employeeService.save(newEmployee);
model.addAttribute("employees", employeeService.findAll());
return "redirect:/employees/all"; // use a redirect
}
Client-Server interaction:
First in your browser you should navigate to the endpoint adding the form-data-object, i.e. GET /employees/add.
Then the form-page should load and you will have a blank object newEmployee to bind all the input-fields to for submitting.
After submitting the form via the submit-click, a POST /employees is sent. The server/controller should receive the form-data as model-attribute in employee parameter and can save it.
The controller-method redirects back to the list-endpoint (same as navigating in browser to) GET /employees/all
<h1>Create employee</h1>
<form th:object="${newEmployee}"
th:action="#{/employees}"
method="post">
<input type="text" name="firstName" id="firstName" th:field="*{firstName}" />
<input type="text" name="lastName" id="lastName" th:field="*{lastName}" />
<button type="submit">Submit new employee</button>
</form>
The th:object attribute refers to the key under which we put our Employee instance in the model (newEmployee in this example). The th:action has the URL for the #PostMapping method. Finally, the method attribute is set to "post" since we want to use the HTTP POST method.
Using th:field=*{…}, we can setup a two-way binding between the HTML input and the field in our form data object.
I need sone help to send the List produtos as parameter to my controller.
<div class="panel-body">
<div th:each="produto : ${produtos}" class="form-group" id="produtos">
<label th:text="${produto.nome}" class="col-sm-2 control-label">
</label>
<div class="col-sm-1">
<input type="text" class="form-control js-number" id="quantPedido"
th:field="*{produto.quantPedido}"/>
</div>
</div>
</div>
My Controller
#RequestMapping(method = RequestMethod.POST)
public String salvar(#Validated Pedido pedido, List<Produto> produtos, Errors errors, RedirectAttributes attributes) {
}
Thanks so Much!
You need to warp yours products list into some bean, then send that bean to the html and fill it. Example:
Bean:
public class Pedido{
#Getter #Setter
private List<Product> products = new ArrayList();
//extra attributes
}
In your controller:
ModelAndView mav = new ModelAndView("yourView");
Pedido pedido = new Pedido();
mav.addObject("pedido", pedido);
In your html:
<form th:object="${pedido}" th:action="#{/savePedido}" method="POST">
//...
<div th:each="produto : ${produtos}" class="form-group" id="produtos">
<label th:text="${produto.nome}" class="col-sm-2 control-label">
</label>
<div class="col-sm-1">
<input class="form-control js-number" id="quantPedido" th:field="*{products[__${produtoStat.index}__].quantPedido}"/>
</div>
</div>
//...
</form>
Hello I'm tryin to developpe a page that can delete users but when I click on submit I have an error Etat HTTP 400 - La requête envoyée par le client était syntaxiquement incorrecte.
Jsp file
</div>
<form method="POST" action="Users">
User ID
<input type="text" name="idUser" /><br><br>
<input type="submit" name="Supprimer" value="Supprimer"/>
</form>
Controller
#RequestMapping(value = "/Users")
public String goUsers(Model model)
{
model.addAttribute("AllUsers", UserS.getAllUsers());
return "Users";
}
#RequestMapping(value = "/Users", method = RequestMethod.POST)
public String goUsers(#ModelAttribute User user,BindingResult result,#RequestParam int id, Map<String, Object> model)
{
UserS.deleteUser(id);
return "Users";
}
thank you
Your controller wrong. You expect oen User and one param with name id but you send one param with name idUser.
Eliminate ModelAttribute and force de name of RequestParam:
#RequestMapping(value = "/Users", method = RequestMethod.POST)
public String goUsers(BindingResult result,#RequestParam(name="idUser") int id, Map<String, Object> model)
{
UserS.deleteUser(id);
return "Users";
}
1.first you need to add modelattribute to your form like this :
Notice how i am using spring forms. You can use them by adding
<%# taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
before DOCTYPE html>
Then you need to add hidden path to attribute "id" so when you controller gets the request you will know which user you will need to delete or edit.
This is example form :
`<form:form method="POST" modelAttribute="User" action="Users">
<form:hidden path="id"/>
<div class="form-group">
<label for="usernameId">Username</label>
<form:input path="username" id="usernameId" class="form-control" />
<form:errors path="username" style="color:red;"></form:errors>
</div>
<div class="form-group">
<label for="fullNameId">Full Name</label>
<form:input path="firstLastName" id="firstLastName" class="form-control"/>
<form:errors path="firstLastName" style="color:red;"></form:errors>
</div>
<div class="form-group">
<label for="passwordId">Password</label>
<form:password path="password" id="passwordId" class="form-control"/>
<form:errors path="password" style="color:red;"></form:errors>
</div>
<div class="form-group">
<label for="emailId">Email</label>
<form:input path="email" id="emailId" class="form-control"/>
<form:errors path="email" style="color:red;"></form:errors>
</div>
<input type="submit" class="btn btn-default" value="Register"/>
</form:form>`
finally you will add to your controller class.
#ModelAttribute("User")
public User getUser(){
return new User();
}
Then you will need to adjust your controller like this :
#RequestMapping(value="/Users", method=RequestMethod.POST)
public String deleteUser(User user){
getRegisterService().deleteUser(user.getId());
return "home";
}
Note : You will have to create class = User : with id attribute(and all others you need). You will also need to create a method for deleting user in your service and repository layer.
P.S. User user in your deleteUser method is actually the same user you created with #modelAttribute annotation.
If you have any additional questions feel free to ask!
I have given you almost exact form i use for register/editing or deleting Users. When you submit form, everything will be saved into object User with annotation #modelAttribute. Hidden id field is crucial here. When you have id, which is primary key you can just create method in repository (something like this)
public void deleteUser(UserJPA userJPA){
getEntityManager().remove(UserJPA);
}
Hope you find this post helpful.
Hello I am developing a CMS using Spring and Hibernate. I have a page displaying existing products and a form which should add a new product to the database. However when I fill out the form and submit it nothing gets saved to the database. Additionally System.out.println does not print anything to the console so I have no idea where is the problem.
jsp page
<form:form class="form-horizontal" method="post" modelAttribute="productForm">
<div class="form-group">
<label class="col-sm-2 control-label"> Product Name:</label>
<div class="col-sm-10">
<form:input class="form-control" type="text" id="productName" name="product name" path="name" />
<br></div></div>
<div class="form-group">
<label class="col-sm-2 control-label"> Serial number:</label>
<div class="col-sm-10">
<form:input class="form-control" type="text" id="productSerial" name="serial number" path="serial" value=" " /></div></div>
<input type="submit" value="Submit">
</form:form>
controller
#RequestMapping(value = "/saveNewContact", method = RequestMethod.POST)
public ModelAndView saveContact(#ModelAttribute("userForm") Product product,
BindingResult result, Model model) {
System.out.println(product);
productService.saveOrUpdate(product);
return new ModelAndView("redirect:/");
}
Service
#Override
public void saveProduct(Product product) {
productDao.saveProduct(product);
}
DAO
the interface implementation has #Transactional(readOnly = false)
public void saveNewProduct(Product product) {
persist(product);
}
The problem was in the form just adding action="saveNewContact" fixed the issue.