I am trying to return the data as a response body in java spring boot when a button is click in an html page.I have a list of countries displayed in my page with an edit button assigned to each. I want to find the data that was clicked by id so I have defined the method in my controller class. That's when the edit button is click, it should take the id of the country in the clicked row and display the information based on that id. When I test the api in Postman, it returns the data correctly but when I called the same api in my html page, it's giving me this error.
org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "/findById/{id=${country.id}}" (template: "country" - line 555, col 26)
at org.thymeleaf.spring5.expression.SPELVariableExpressionEvaluator.evaluate(SPELVariableExpressionEvaluator.java:292) ~[thymeleaf-spring5-3.0.14.RELEASE.jar:3.0.14.RELEASE]
Caused by: org.springframework.expression.spel.SpelParseException: Expression [/findById/{id=${country.id}}] #0: EL1070E: Problem parsing left operand
This is my data class
Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Country {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String code;
private String capital;
private String description;
private String nationality;
private String continent;
}
My Controller class
#Controller
public class CountryController {
#Autowired
private CountryService countryService;
#GetMapping("/countries")
public String getCountry(Model model){
List<Country> countryList = countryService.getAllCountry();
model.addAttribute("countries",countryList);
return "country";
}
#PostMapping("/countries/addNew")
public String saveInfo(Country country){
countryService.saveCountryInfo(country);
return "redirect:/countries";
}
#GetMapping("/findById/{id}")
#ResponseBody
public ResponseEntity<Country> getCountryById(#PathVariable("id") Long countryId){ //Bind PathVariable id to id
return ResponseEntity.ok(countryService.getCountryById(countryId)) ;
}
#GetMapping("/country/code/{code}")
public Country getCountryCode(#PathVariable("code") String code){
return countryService.getCountryByCode(code);
}
}
My Service class
#Service
public class CountryService {
#Autowired
private CountryRepository countryRepository;
public List<Country> getAllCountry() {
return countryRepository.findAll();
}
public void saveCountryInfo(Country country){
countryRepository.save(country);
}
public Country getCountryById(Long id){
return countryRepository.findById(id).get();
}
public Country getCountryByCode(String code){
return countryRepository.findByCode(code);
}
}
My Repository class
#Repository
public interface CountryRepository extends JpaRepository<Country,Long> {
public Country findByCode(String code);
}
Here is the html code
<section class="section dashboard">
<div class="row">
<!-- Left side columns -->
<div class="row">
<div class="col-lg-9 col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
<!-- Image background -->
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addModal" data-whatever="#mdo">Add A Country</button>
<h1>List of Country</h1>
<table class="table">
<thead>
<tr>
<th>Id</th>
<th>Code</th>
<th>Capital</th>
<th>Description</th>
<th>Nationality</th>
<th>Continent</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr th:each="country:${countries}">
<td th:text="${country.id}"></td>
<td th:text="${country.code}">Code</td>
<td th:text="${country.capital}">Capital</td>
<td th:text="${country.description}">Description</td>
<td th:text="${country.nationality}">Nationality</td>
<td th:text="${country.continent}">Continent</td>
<td>
<div class="btn-group">
<a th:href="${/findById/{id=${country.id}}}" class="btn btn-primary" id="editButton" data-bs-toggle="modal" data-bs-target="#editModal">Edit</a>
</div>
</td>
</tr>
</tbody>
</table>
</div><!-- End of Image background -->
</div><!-- End Left side columns -->
</div>
</div>
</div>
</section>
Related
I am new to spring boot and trying to create a simple ToDo Application using spring boot and JSP. I am trying to show the list of todo's in my JSP but it is not reflecting. I tried all the ways whereas on putting breakpoint in IntelliJ idea, I am able to see the list of todo's in my repo.
ToDoView.jsp
<%# include file="header.jsp"%>
<%# include file="navigation.jsp"%>
<div class="container">
<div>
<a type="button" class="btn btn-primary btn-md" href="/add-todo">Add Todo</a>
</div>
<br>
<div class="panel panel-primary">
<div class="panel-heading">
<h3>List of TODO's</h3>
</div>
<div class="panel-body">
<table class="table table-striped">
<thead>
<tr>
<th width="40%">Description</th>
<th width="40%">Target Date</th>
<th width="20%"></th>
</tr>
</thead>
<tbody>
${todolist}
<c:forEach items="${todolist}" var="todo">
<tr>
<td>${todo.description}</td>
<td>${todo.getDescription()}</td> <td><fmt:formatDate value=${todo.getToDoDate()} pattern="dd/MM/yyyy" /></td>
<td><a type="button" class="btn btn-success" href="/update-todo?id=${todo.getId()}">Update</a>
<a type="button" class="btn btn-warning" href="/delete-todo?id=${todo.getId()}">Delete</a></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</div>
</div>
<%# include file="footer.jsp"%>
HomeController.java
package com.toDoApp.ToDoApp;
#Controller
public class HomeController
{
#Autowired
ToDoRepo repo;
#RequestMapping("/")
public static String home()
{
return "Home";
}
#GetMapping(path = "/list-todos", produces = {"application/json"})
public String toDoView(Model m)
{
m.addAttribute("todolist", repo.findAll());
return "ToDoView";
}
}
ToDoRepo.java
public interface ToDoRepo extends JpaRepository<ToDoList, Integer>
{
}
ToDoList.java
package com.toDoApp.ToDoApp;
import javax.persistence.*;
import java.util.Date;
#Entity
public class ToDoList
{
#Id
private int id;
private String description;
private Date toDoDate;
public ToDoList()
{
super();
}
public ToDoList(String description, Date toDoDate)
{
super();
this.description = description;
this.toDoDate = toDoDate;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Date getToDoDate() {
return toDoDate;
}
public void setToDoDate(Date toDoDate) {
this.toDoDate = toDoDate;
}
}
I tried all other ways but didn't able to find anything. Please help me out.
Thank You
Add the jasper Dependency
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
and change the td tags like this
${todo.getDescription()}
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
try adding jasper dependency
i need help
I'm trying to create a filter for a store project, using spring, thymeleaf and bootstrap in which the filter will receive from the Product class the value of the "status" attribute which is an enum, however when using the expression language "${produto.status.name()}" it returns me the exception of the topic I will post the code of the html page, the model class and the enum.
HTML snippet
<main>
<div class="container" >
<h2 class="text-center">Produtos</h2>
<div class="row">
<div class=" d-flex align-items-stretch" style="flex-direction: row; flex-wrap: wrap; justify-content:center; ">
<div class=" col-md-4 col-xl-3 col-lg-3 col-sm-6 mx-1 my-2" th:each="produto : ${produtos}" >
<th:block th:switch="${produto.status.name()}">
<div th:case="'ACESSORIOS'" class="card border-dark bg-dark text-white text-center"></div>
<div th:case="'CELULAR'" class="card border-primary bg-dark text-white text-center">></div>
<div th:case="'COMPUTADOR'" class="card border-light bg-dark text-white text-center"></div>
</th:block>
Product class
#Entity
public class Produto {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String nome;
private BigDecimal preco;
private Integer quantidade;
private String descricao;
private String urlImagem;
#Enumerated(EnumType.STRING)
private Statuscategoria status;
public Statuscategoria getStatus() {
return status;
}
public void setStatus(Statuscategoria status) {
this.status = status;
}
... other getters and setters
Enum code
public enum Statuscategoria {
CELULAR, COMPUTADOR, ACESSORIOS;
}
This is the error
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1011E: Method call: Attempted to call method name() on null context object
Could anyone take a look of this error and help me? I spent 2 hours to find problem but I didn't find solution.
Listing data works, but problem is when adding jsp with action="saveCategory.
org.springframework.beans.NotReadablePropertyException: Invalid property 'categoryName' of bean class [java.util.ArrayList]: Bean property 'categoryName' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
JSP:
<c:forEach var="tempInvoiceCategory" items="${invoiceCategory}">
<tr>
<td>${tempInvoiceCategory.categoryName}</td>
<td>
<button type="button" class="btn btn-info">Edit</button>
<button type="button" class="btn btn-danger">Delete</button>
</td>
</tr>
</c:forEach>
<form:form class="forms-sample" action="saveCategory" modelAttribute="invoiceCategory" method="POST">
<div class="form-group">
<label>Nazwa Kategorii</label>
<form:input path="categoryName" type="text" class="form-control"/>
</div>
<button type="submit" class="btn btn-primary mr-2">Submit</button>
</form:form>
Entity:
#Entity
#Table(name="T_INVOICE_CATEGORY")
public class InvoiceCategory {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="ID_CATEGORY")
private int id;
#Column(name="CATEGORY_NAME")
private String categoryName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCategoryName() {
return categoryName;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
#Override
public String toString() {
return "InvoiceCategory [id=" + id + ", categoryName=" + categoryName + "]";
}
}
Controller:
#Controller
#RequestMapping("/invoice")
public class InvoiceController {
#Autowired
private InvoiceCategoryService invoiceCategoryService;
#GetMapping("/listCategory")
public String listCategory(Model theModel) {
List<InvoiceCategory> invoiceCategory = invoiceCategoryService.getInvoiceCategory();
theModel.addAttribute("invoiceCategory",invoiceCategory);
return "add-invoice-category";
}
#PostMapping("/saveCategory")
public String saveCustomer(#ModelAttribute("invoiceCategory") InvoiceCategory theInvoiceCategory) {
invoiceCategoryService.saveInvoiceCategory(theInvoiceCategory);
return "redirect:/customer/list";
}
}
I think problem is that model attribute invoiceCategory is List<InvoiceCategory> and in same JSP you try build list of items and form which try access to this item, but it is List<InvoiceCategory>:
<form:form class="forms-sample" action="saveCategory" modelAttribute="invoiceCategory" method="POST">
<div class="form-group">
<label>Nazwa Kategorii</label>
<form:input path="categoryName" type="text" class="form-control"/>
</div>
<button type="submit" class="btn btn-primary mr-2">Submit</button>
</form:form>
Try comment this part and run application again.
I've tried to create an object with another object inside, with a form but the Object picked from a dropdown list gets converted into a String when returned from the Thymeleaf form.
Those are the entities in my project, with an 1:n relatioship between them:
Entity User
//imports
#Entity
#Table(name = "USERS")
public class User {
#Id
#GeneratedValue
#Column( name ="USER_ID")
private int id;
#Column( name ="username")
private String username;
#Column( name ="password")
private String password;
#Column( name ="email")
private String email;
#OneToMany(fetch= FetchType.LAZY, mappedBy="user", cascade = CascadeType.ALL)
private List<Post> posts;
//setter & getters & toString
}
Entity Post
//imports
#Entity
#Table(name="POSTS")
public class Post {
#Id
#GeneratedValue
#Column(name="POST_ID")
private int id;
#Column(name="tittle")
private String tittle;
#Column(name="text")
private String text;
#ManyToOne
#JoinColumn(name="USER_ID",referencedColumnName="USER_ID")
private User user;
//getters & setters & toString
To create a new Post:
//In Controller
#RequestMapping(value = "/posts/new")
public String newPost(Model model) {
model.addAttribute("post", new Post());
model.addAttribute("users", userService.list());
return "addPost";
}
Which returns the template that has this form:
<form th:action="#{/savePost}" th:object="${post}" method="post">
<tr>
<td><input type="hidden" th:field="${post.id}" /></td>
</tr>
<tr>
<td>Titulo</td>
<td>Texto</td>
<td>Usuario</td>
</tr>
<tr>
<td><input type="text" th:field="${post.tittle}"
th:value="${post.tittle}" /></td>
<td><input type="text" th:field="${post.text}"
th:value="${post.text}" /></td>
<td><select th:field="${post.user}">
<option th:each="user : ${users}" th:text="${user.username}"
th:value="${user.id}"></option>
</select></td>
</tr>
<tr>
<td colspan="3"><input class="btn btn-primary" type="submit"
value="GUARDAR"></td>
</tr>
</form>
The action attribute in the form calls:
#PostMapping("/savePost")
//#RequestMapping(value = "users/save",method = RequestMethod.POST)
public String savePost(#ModelAttribute Post post) {
postService.add(post);
return "redirect:/posts";
}
At this point, I try to create a Post and select a User from the dropdown but when attempting to save it gives me this error:
Field error in object 'post' on field 'user': rejected value [16]; codes [typeMismatch.post.user,typeMismatch.user,typeMismatch.com.julian.bootmvchibernate.model.User,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [post.user,user]; arguments []; default message [user]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'com.julian.bootmvchibernate.model.User' for property 'user'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [#javax.persistence.ManyToOne #javax.persistence.JoinColumn com.julian.bootmvchibernate.model.User] for value '16'; nested exception is java.lang.NullPointerException]
So I tried to implement a Formatter like so:
public class UserFormatter implements Formatter<User> {
#Autowired
#Qualifier("userService")
public GeneralService<User> userService;
#Override
public String print(User object, Locale locale) {
return (object != null ? object.getUsername() : "");
}
#Override
public User parse(String text, Locale locale) throws ParseException {
final Integer userId = Integer.parseInt(text);
return userService.get(userId);
}
}
Registering it:
#SpringBootApplication
public class BootmvchibernateApplication implements WebMvcConfigurer{
#SuppressWarnings("unchecked")
#Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatter(new UserFormatter());
}
public static void main(String[] args) {
SpringApplication.run(BootmvchibernateApplication.class, args);
}
}
But when this approach is tried the next error is found, this just uppon accessing the /post/new direcction (the template addPost doesn't work):
An error happened during template parsing (template: "class path resource [templates/addPost2.html]")
org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/addPost2.html]")
.....
Caused by: org.attoparser.ParseException: Error during execution of processor 'org.thymeleaf.spring5.processor.SpringOptionFieldTagProcessor' (template: "addPost2" - line 43, col 8)
.....
Caused by: org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [#javax.persistence.ManyToOne #javax.persistence.JoinColumn com.julian.bootmvchibernate.model.User] for value '2'; nested exception is java.lang.NullPointerException
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:47)
If any more information is needed, tell me and I post it.
EDIT: this is the repository if someone is interested.
github.com/JulianBautistaVelez/JPA_Spring_Excercise
The problem is in your addPost.html except that everything is ok now.
<table>
<form th:action="#{/posts/new/mod}" th:object="${post}"
method="POST">
<tr>
<td><input type="hidden" th:field="${post.id}" /></td>
</tr>
<tr>
<td>Titulo</td>
<td>Texto</td>
<td>Usuario</td>
</tr>
<tr>
<td><input type="text" th:field="${post.tittle}"
th:value="${post.tittle}" /></td>
<td><input type="text" th:field="${post.text}"
th:value="${newPost.text}" /></td>
<!-- <td><select th:field="${newPost.user}">
<option th:each="user : ${users}" th:text="${user.username}"
th:value="${user.id}"></option>
</select></td> -->
<td><select th:field="*{user}" class="form-control">
<option th:each="user: ${users}"
th:value="${user.id}" th:text="${user.username}"></option>
</select></td>
</tr>
<tr>
<td colspan="3"><input class="btn btn-primary" type="submit"
value="GUARDAR"></td>
</tr>
</form>
</table>
I changed newPost as post , because you are adding in here as post model.
#RequestMapping(value = "/posts/new")
public String newPost(Model model) {
logger.info("-- en NEW Usuario");
model.addAttribute("post", new Post());
model.addAttribute("users", userService.list());
logger.info("-- -- -- LISTA DE USUARIOS -- -- --");
System.out.println(userService.list());
return "addPost";
}
I have a table cards which is related to some other master tables (divisions and units) with one to one relation.
On UI I am showing drop downs to select the division and unit values for card, this is the function to create form in CardController.java
#RequestMapping(value = "/addCardForm", method = RequestMethod.GET)
public String addCardForm(ModelMap map)
{
map.addAttribute("divisions", divisionService.getAllDivisions());
map.addAttribute("units", unitService.getAllUnits());
return "admin/addCard";
}
addCard.jsp :
<form:form method="post" action="addCard">
<table cellspacing="10" id="card-table">
<tr>
<td><label for="division" class="control-label">Division : </label></td>
<td><select name="division" class="selectpicker">
<option>Select</option>
<c:forEach items="${divisions}" var="division">
<option value="${division.id}">${division.name}</option>
</c:forEach>
</select></td>
</tr>
<tr>
<td><label for="unit" class="control-label">Unit : </label></td>
<td><select name="unit" class="selectpicker">
<option>Select</option>
<c:forEach items="${units}" var="unit">
<option value="${unit.id}">${unit.name}</option>
</c:forEach>
</select></td>
</tr>
<tr>
<td><button type="submit" class="btn btn-primary">Submit</button></td>
<td></td>
</tr>
</table>
</form>
Drop downs are populated with the data but on submitting form its not setting division or unit to the cardEntity object they are set to null, this is the addCard function in controller :
#RequestMapping(value = "/addCard", method = RequestMethod.POST)
public String addCard(#ModelAttribute(value="card") CardEntity card, BindingResult result)
{
cardService.addCard(card);
//card.getDivision(); -- this is null
return "redirect:/card";
}
There are other fields which are added in the card except drop downs.
CardEntity.java
#Entity
#Table(name="cards")
#Proxy(lazy=false)
public class CardEntity {
#Id
#Column(name="id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#OneToOne
#JoinColumn(name="division_id", referencedColumnName="id")
private DivisionEntity division;
#OneToOne
#JoinColumn(name="unit_of_qty_id", referencedColumnName="id")
private UnitEntity units;
public DivisionEntity getDivision() {
return division;
}
public void setDivision(DivisionEntity division) {
this.division = division;
}
public UnitEntity getUnits() {
return units;
}
public void setUnits(UnitEntity units) {
this.units = units;
}
}
Edit :
I have to set divisionEntity and unitEntity manually :
#RequestMapping(value = "/addCard", method = RequestMethod.POST)
public String addCard(HttpServletRequest request, #ModelAttribute(value="card") CardEntity card, BindingResult result)
{
card.setDivision(divisionService.findOne(Integer.parseInt(request.getParameter("division"))));
card.setUnit(unitService.findOne(Integer.parseInt(request.getParameter("unit"))));
cardService.addCard(card);
return "redirect:/card";
}
So I guess the problem is divisionEntity and unitEntity objects are not set in card after submitting the form.
You can do this too:
<tr>
<td><label for="division.id" class="control-label">Division : </label></td>
<td><select name="division.id" class="selectpicker">
<option>Select</option>
<c:forEach items="${divisions}" var="division">
<option value="${division.id}">${division.name}</option>
</c:forEach>
</select></td>
</tr>
<tr>
<td><label for="unit.id" class="control-label">Unit : </label></td>
<td><select name="unit.id" class="selectpicker">
<option>Select</option>
<c:forEach items="${units}" var="unit">
<option value="${unit.id}">${unit.name}</option>
</c:forEach>
</select></td>
</tr>
The rest is same as your original. But:
Make sure you have a default constrcutor in Division class;
The CardEntity should have getUnit() and setUnit(), not getUnits() and setUnits().
Hope it help.