I'm getting this exceptions when trying to update an object with a PostMapping:
Caused by: org.thymeleaf.exceptions.TemplateProcessingException:
Exception evaluating SpringEL expression: "libro.id" (template:
"formulario-modificar-libros-p" - line 18, col 15)
Caused by:
org.springframework.expression.spel.SpelEvaluationException: EL1007E:
Property or field 'id' cannot be found on null
Form:
<form th:action="#{/libros/modificar-libro-d/__${libro.id}__}" method="post">
<input hidden th:value="${libro.id}" name="id">
<div class="form-group">
<label>ISBN del libro</label> <input th:value="${libro.isbn}" type="number" class="form-control" name="isbnLibro" required>
</div>
<div class="form-group">
<label>Título del libro</label> <input th:value="${libro.titulo}" type="text" class="form-control" name="tituloLibro" required>
</div>
<div class="form-group">
<label>Año del libro</label> <input th:value="${libro.anio}" type="number" class="form-control" name="anioLibro">
</div>
<div class="form-group">
<label>Ejemplares del libro</label> <input th:value="${libro.ejemplares}" type="number" class="form-control" name="ejemplaresLibro" required>
</div>
<div>
<label>Autor</label>
<select class="form-select" aria-label="Default select example" name="nombreAutor">
<option th:each="autor:${autores}"
th:text="${autor.nombre}"
th:value="${autor.nombre}"
>
</option>
</select>
</div>
<div>
<label>Editorial</label>
<select class="form-select" aria-label="Default select example" name="nombreEditorial">
<option th:each="editorial:${editoriales}"
th:text="${editorial.nombre}"
th:value="${editorial.nombre}"
>
</option>
</select>
</div>
<button type="submit" class="btn btn-primary">Aceptar</button>
</form>
Controller
#PostMapping("/modificar-libro-d/{id}")
public String modificarLibroM(#PathVariable String id, #RequestParam Long isbnLibro, #RequestParam String tituloLibro, #RequestParam Integer anioLibro,
#RequestParam Integer ejemplaresLibro, #RequestParam String nombreAutor, #RequestParam String nombreEditorial){
try {
libroServicio.modificarLibro(id, isbnLibro, tituloLibro, anioLibro, ejemplaresLibro, nombreAutor, nombreEditorial);
return "redirect:/libros/lista-libros-d";
} catch (Exception e) {
return "formulario-modificar-libros-p";
}
}
#GetMapping("/modificar-libro-d/{id}")
public String formularioModificarLibrosM(#PathVariable String id, ModelMap modelo) {
modelo.addAttribute("libro", libroServicio.buscarLibroPorId(id));
modelo.addAttribute("autores", autorServicio.listarAutoresAlta());
modelo.addAttribute("editoriales", editorialServicio.listarEditorialesAlta());
return "formulario-modificar-libros-p";
}
In the entity, all fields have their getters and setters correctly.
It looks like your code is okay but the find by id method on your book service returns null for the given id. Put a breakpoint on it and run it in debug. If a libro object must always be present, consider defining your libroServicio.buscarLibroPorId(id) method such that an exception is thrown for a missing id and/or set up error handling in your thymeleaf template.
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 have problems with sending data to one of my tables.
Below you can see my methods: one that shows the template with form, and the second that shoud add this action.
#GetMapping("/addaction/{id}")
public String addAction(Model model, #PathVariable("id") int id ) {
Optional<PlantEntity> plantEntity = plantService.getPlantById(id);
if (plantEntity.isPresent()) {
model.addAttribute("plant", plantEntity.get());
}
return "addaction";
}
#PostMapping("/addaction/{id}")
public String addAction(#ModelAttribute ActionForm actionForm,
#PathVariable("id") int plantId) {
if(!userService.isLogin()) {
return "redirect:/";
}
actionService.addAction(actionForm, plantId);
return "redirect:/plant/"+plantId;
}
Here is my method in Service:
public void addAction (ActionForm actionForm, int plantId) {
PlantEntity plantEntity = new PlantEntity();
plantEntity.setId(plantId);
ActionEntity act = new ActionEntity();
act.setName(actionForm.getName());
act.setDescription(actionForm.getDescription());
act.setPlant(plantEntity);
act.setUser(userService.getUserData());
act.setMonth(actionForm.getMonth());
actionRepository.save(act);
}
and my template: addaction.html
<form method="post" action="'/addaction/'+${plant.getId()}"
th:object="${actionForm}">
<div class="form-group">
<label for="exampleInputEmail1">Name of activity</label> <input
type="text" th:field="*{name}" class="form-control"
id="exampleFormControlInput1" aria-describedby="emailHelp"
placeholder="Name your work">
</div>
<div class="form-group">
<label for="exampleFormControlTextarea1">What you gonna
do?</label>
<textarea class="form-control" th:field="*{description}"
id="exampleFormControlTextarea1" rows="4"></textarea>
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<label class="input-group-text" for="inputGroupSelect01">Month of activity</label>
</div>
<select class="custom-select" th:field="*{month}"
id="inputGroupSelect01">
<option value="1">January</option>
<option value="2">February</option>
<option value="3">March</option>
<option value="4">April</option>
<option value="5">May</option>
<option value="6">June</option>
<option value="7">July</option>
<option value="8">August</option>
<option value="9">September</option>
<option value="10">October</option>
<option value="11">November</option>
<option value="12">December</option>
</select>
</div>
<button type="submit" class="btn btn-dark">Action!</button>
</form>
The main problem is: when I try to addAction the result is:
http://localhost:8080/addaction/'/addaction/'+$%7Bplant.getId()%7D
There is some kind of loop. What am I doing wrong? Thank you for your time!
You don't have to pass '. spring expression language will take without ' also.
Try removing like below.
action="/addaction/${plant.getId()}"
Refer thymeleaf-construct-url-with-variable
I can't seem to figure out why I keep getting a 400 bad request. Before I got 400 bad request, the form had "id" instead of "name". When the form had "id" I got 200 but didn't update my database. Now, I get the error and nothing seems to be working.
Here's my controller:
#RequestMapping(value = "/registration", method = RequestMethod.POST)
public String registration(MemberVO vo, Model model) {
System.out.println(vo);
logger.info("regist post...");
logger.info(vo.toString());
try {
mservice.insertMember(vo);
} catch (Exception e) {
e.printStackTrace();
}
return "/register_success";
}
#RequestMapping(value = "/registration", method = RequestMethod.GET)
public void registrationGet(MemberVO vo, Model model) {
}
Here's my form:
<form role = "form" method ="post">
<div class="form-group">
<input type="email" class="form-control" name = "username" placeholder="Email" required/>
<span><i class="fa fa-envelope"></i></span>
</div>
<div class="form-group">
<input type="password" class="form-control" name = "password" placeholder="Password" required/>
<span><i class="fa fa-lock"></i></span>
</div>
<div class="form-group">
<input type="text" class="form-control" name = "firstname" placeholder="firstname" required/>
<span><i class="fa fa-user"></i></span>
</div>
<div class="form-group">
<input type="text" class="form-control" name = "lastname" placeholder="lastname" required/>
<span><i class="fa fa-user"></i></span>
</div>
<div class="form-group">
<input type="text" class="form-control" name = "phonenum" placeholder="Phone Number" required/>
<span><i class="fa fa-user"></i></span>
</div>
<div class="form-group">
<input type="text" class="form-control" name = "birthday" placeholder="Birthday, ex) 1986-06-08" required/>
<span><i class="fa fa-user"></i></span>
</div>
<div class="form-group">
<input type="text" class="form-control" name = "destination" placeholder="where would you like to go?" required/>
<span><i class="fa fa-user"></i></span>
</div>
<button type = "submit" class="btn btn-orange btn-block">Sign Up</button>
</form>
We can not tell you the exact problem as you have not attached your modal class MemberVO but you can check below.
1.There is no action attribute in your form.
2.Ensure that all the fields present in Modal class should also be present in the form with the same name.
I am testing a spring boot application which uses thymeleaf, but I could not find any docs explaining how to send select options values from thymeleaf to a spring boot service class.
Basically, what I am trying to achieve is get values from the select tag so that I can insert them to the database through the method below:
Please note: this method is in the service class => it has both get and post mapping in the controller class.
public void addNewJob(JobPostEntity jobPostEntity, #RequestParam(value="selectCategory") String selectCategory) {
jobPostEntity.setJobcategory("test");
jobPostRepository.save(jobPostEntity);
}
the thymeleaf file is:
<form th:action="#{/newjob}" th:object="${addNewJob}" method="post">
<div class="form-group">
<label for="">Offer Title</label>
<input type="text" th:field="*{jobtitle}" class="form-control" placeholder="Entre Offer Title">
<small class="form-text text-muted">We'll never share your email
with anyone else.</small>
</div>
<div class="form-group">
<label >Company Name</label>
<input type="text" th:field="*{jobcompany}" class="form-control" placeholder="Enter Company Name">
</div>
<div class="form-group dropdown">
<label for="sel1">Choose Category (select one):</label>
<select name="*selectCategory"
class="form-control" id="selectCategory"
onchange="getSelectedValue();" th:field="*{selectCategory}">
<option value="">Select Option</option>
<option value="software_engineer">Software Engineer</option>
<option value="graphic_design ">Graphic Design</option>
<option value="customer_service ">Customer Service</option>
<option value="marketing" >Marketing</option>
<option value="healthcare">Health Care</option>
</select>
</div>
<div class="form-group">
<label for="exampleInputPassword1">Offer</label>
<textarea class="form-control" th:field="*{jobtext}" placeholder="Describe your job offer"></textarea>
</div>
<button type="submit" class="btn btn-primary">Submit Offer</button>
</form>
First, you need to properly configure your controller class. I assume this is where your addNewJob method is located, so I use this in my example.
You need to have a #GetMapping (which is the same as #RequestMapping(method = RequestMethod.GET) that returns the name of the view (this is your thymeleaf file - in the example below I used jobForm.html for this) and maps to a specific path (/test in example).
#GetMapping("/test")
public String getTestView() {
return "jobform";
}
You will also need a method which creates/retrieve the model object you use to fill the form with. This is mapped as th:object=${addNewJob} in the form:
#ModelAttribute(value = "addNewJob")
public JobPostEntity newEntity() {
return new JobPostEntity();
}
Finally, you will need a method with #PostMapping, that's called when you submit your form. In your example, that's mapped to /newjob, so I used this too:
#PostMapping(value = "/newjob")
public void addNewJob(
#ModelAttribute("addNewJob") final JobPostEntity myEntity) {
System.out.println("got dto: " + myEntity);
System.out.println("selectCategory: " + myEntity.getSelectedCategory());
}
To summarize the controller would look something like this:
#Controller
public class TestController {
#GetMapping("/test")
public String getTestView() {
return "jobform";
}
#PostMapping(value = "/newjob")
public void addNewJob(
#ModelAttribute("addNewJob") final JobPostEntity myEntity) {
System.out.println("got dto: " + myEntity);
System.out.println("selectCategory: " + selectCategory);
}
#ModelAttribute(value = "addNewJob")
public JobPostEntity newEntity() {
return new JobPostEntity();
}
}
As for the select option to work, I'd also put that field in the modelAttribute, so you don't have to treat them separately:
public class JobPostEntity {
private String jobtitle;
private String jobcompany;
private String jobtext;
private String selectCategory;
//getters/setters
}
Your pasted html code also contains a few issues:
you don't have the opening tags for form
the select tag doesn't have thymeleaf mapping for it (th:field)
the input tags doesn't have closing elements
The fixed version, which works for me, looks something like this (excluding the body/head/etc wrapper tags):
<form th:action="#{/newjob}" th:object="${addNewJob}" method="post">
<div class="form-group">
<label for="">Offer Title</label>
<input type="text" th:field="*{jobtitle}" class="form-control" placeholder="Entre Offer Title" />
<small class="form-text text-muted">We'll never share your email
with anyone else.</small>
</div>
<div class="form-group">
<label >Company Name</label>
<input type="text" th:field="*{jobcompany}" class="form-control" placeholder="Enter Company Name"/>
</div>
<div class="form-group dropdown">
<label for="sel1">Choose Category (select one):</label>
<select name="*selectCategory"
class="form-control" id="selectCategory"
onchange="getSelectedValue();"
th:field="*{selectCategory}">
<option value="">Select Option</option>
<option value="software_engineer">Software Engineer</option>
<option value="graphic_design ">Graphic Design</option>
<option value="customer_service ">Customer Service</option>
<option value="marketing" >Marketing</option>
<option value="healthcare">Health Care</option>
</select>
</div>
<div class="form-group">
<label for="exampleInputPassword1">Offer</label>
<textarea class="form-control" th:field="*{jobtext}" placeholder="Describe your job offer"></textarea>
</div>
<button type="submit" class="btn btn-primary">Submit Offer</button>
</form>
if you start your application and type http://localhost:8080/test (if you use the default context path / port ) in your browser, the form should appear and work as expected.
also, you can find a pretty good tutorial on here http://www.baeldung.com/thymeleaf-in-spring-mvc
I have implemented a registration process where you can send user data to the controller via post request.
The post request works fine, however now I want to pass another value (role, Long) from the form to the controller that is not an attribute of the user model.
That part is not working.
Does anyone know why?
HTML:
<form action="add_user" method="post" class="form-horizontal" th:object="${user}">
<div class="form-group">
<div class="col-sm-offset-1 col-sm-10">
<input th:field="*{username}" class="form-control" placeholder="Person ID" type="text" name="id" id="id"/>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-10">
<input th:field="*{firstName}" class="form-control" placeholder="First Name" type="text" name="firstname" id="firstname"/>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-10">
<input th:field="*{lastName}" class="form-control" placeholder="Last Name" type="text" name="lastname" id="lastname"/>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-10">
<input th:field="*{password}" class="form-control" placeholder="Password" type="password" name="password" id="password"/>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-10">
<select th:field="${role}" class="form-control" id="role">
<option value="1">Admin</option>
<option value="2" >User</option>
</select>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-10">
<button type="submit" class="btn btn-success" value="Submit">Save</button>
</div>
</div>
</form>
Controller:
#RequestMapping(value = "/users", method = RequestMethod.GET)
public String showUsers(Model model)
model.addAttribute("user", new User());
model.addAttribute("role", new Long(2));
return "users";
}
And:
#RequestMapping(value = "/add_user", method = RequestMethod.POST)
public String handleNewUser(#ModelAttribute("user") User user, BindingResult bindingResult, Model model, long role) {
if (user != null) {
System.out.println(role);
userService.save(user);
}
return "redirect:/users";
}
th:field="${role}" means name of field in the model object, not its value. You probably want to write th:value="${role}" instead of this.