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
Related
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>
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 am new to Spring Boot and I am am working on creating connecting the front of an application to the back at the moment.
I used this site https://spring.io/guides/gs/validating-form-input/
to work on a simple example and it worked fine. It uses the names 'name' and 'age' for the two fields, and getAge, and getName etc for getters and setters.
This code works:
HTML form:
<html>
<body>
<form action="#" th:action="#{/}" th:object="${personForm}" method="post">
<table>
<tr>
<td>Name:</td>
<td><input type="text" th:field="*{name}" /></td>
<td th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name Error</td>
</tr>
<tr>
<td>Age:</td>
<td><input type="text" th:field="*{age}" /></td>
<td th:if="${#fields.hasErrors('age')}" th:errors="*{age}">Age Error</td>
</tr>
<tr>
<td><button type="submit">Submit</button></td>
</tr>
</table>
</form>
</body>
</html>
Person java class
public class PersonForm {
#NotNull
#Size(min=2, max=30)
private String name;
#NotNull
#Min(18)
private Integer age;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String toString() {
return "Person(Name: " + this.name + ", Age: " + this.age + ")";
}
}
Controller
#Controller
public class WebController extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/results").setViewName("results");
}
#GetMapping("/")
public String showForm(PersonForm personForm) {
return "form";
}
#PostMapping("/")
public String checkPersonInfo(#Valid PersonForm personForm, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "form";
}
return "redirect:/results";
}
}
I have simply changed the names of the variables to the below: (2 strings instead of an int and a string). The following error is displayed when I try to view the page: Error during execution of processor 'org.thymeleaf.spring4.processor.attr.SpringInputGeneralFieldAttrProcessor' (form:33)
Line 33 has " th:field="*{testcolumn}" " on it.
Is there certain naming conventions you must follow with thymeleaf perhaps? I cannot find information about it. Thanks
Person class:
#NotNull
#Size(min=2, max=30)
private String testcolumn;
#NotNull
private String testcolumntwo;
public String getTestcolumn() {
return this.testcolumn;
}
public void setTestcolumn(String testcolumn) {
this.testcolumn = testcolumn;
}
public String getTestcolumntwo() {
return testcolumntwo;
}
public void setTestcolumntwo(String testcolumntwo) {
this.testcolumntwo = testcolumntwo;
}
public String toString() {
return "Person(Name: " + this.testcolumntwo + ", Age: " + this.testcolumntwo + ")";
}
HTML Form:
<form action="#" th:action="#{/}" th:object="${personForm}" method="post">
<table>
<tr>
<td>Name:</td>
<td><input type="text" th:field="*{testcolumn}" /></td>
<td th:if="${#fields.hasErrors('testcolumn')}" th:errors="*{testcolumn}">Name Error</td>
</tr>
<tr>
<td>Age:</td>
<td><input type="text" th:field="*{testcolumntwo}" /></td>
<td th:if="${#fields.hasErrors('testcolumntwo')}" th:errors="*{testcolumntwo}">Age Error</td>
</tr>
<tr>
<td><button type="submit">Submit</button></td>
</tr>
</table>
</form>
I am new to Thymeleaf, I try to execute a simple form submittion example using Thymeleaf and Spring MVC. I wrote the code according to Thymeleaf documentation. But I am getting null values in the controller.
<form action="thymeleafexample/thymeleaf.html" th:action="#{/thymeleaf}"
th:object="${loginModel}" method="post">
<table>
<tr>
<td th:text="#{emp.empId.label}">Emp ID</td>
<td><input type="text" th:field="*{empId}"/></td>
</tr>
<tr>
<td th:text="#{emp.empName.label}">Emp Name</td>
<td><input type="text" th:field="*{empName}"/></td>
</tr>
<tr>
<td>
<button type="submit" name="save" th:text="#{${'.emp.button.label'}}">submit</button>
</td>
</tr>
</table>
</form>
and my Controller is
#RequestMapping(value = "/thymeleaf", params = {"save"})
public String save(#Validated LoginModel loginModel, final BindingResult bindingResult, ModelMap model) {
if (bindingResult.hasErrors()) {
return "thymeleaf";
}
System.out.println(loginModel);
System.out.println(loginModel.getEmpName());
return "/thymeleaf";
}
and my Model class is
public class LoginModel {
private String empName;
private int empId;
public void setEmpId(int empId) {
this.empId = empId;
}
public int getEmpId() {
return empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
}
I was having the same problem and as OP mentioned, creating a constructor for the POJO(Model) class with necessary member fields and using th:field=*{foo} instead of th:value=${foo} solved my issue.
After reading tutorials and docs I still do not understand the mechanism behind binding certain Object properties in SpringMVC + hibernate.
Suppose we have a class Poem:
package com.test.poems.model;
import com.tastyminerals.poems.model.Author;
import com.tastyminerals.poems.model.Genre;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
#Entity
#Table(name = "P_POEM")
public class Poem {
#Id
#GeneratedValue
#Column(name="ID")
private Integer id;
#Column(name="TITLE")
private String title;
#Column(name="BODY")
private String body;
#Column(name="DATE")
private String date;
#ManyToOne
#JoinColumn(name="ID", referencedColumnName="ID_AUTHOR", insertable = false, updatable = false)
private Author author;
#ManyToOne
#JoinColumn(name="ID", referencedColumnName="ID_GENRE", insertable = false, updatable = false)
private Genre genre;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Author getAuthor() {
return author;
}
public Genre getGenre() {
return genre;
}
public void setAuthor(Author author) {
this.author = author;
}
public void setGenre(Genre genre) {
this.genre = genre;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
}
and a class Author:
#Entity
#Table(name = "AUTHORS")
public class Author {
#Id
#GeneratedValue
#Column(name="ID_AUTHOR")
private Integer id;
#Column(name="NAME")
private String name;
/* getters and setters */
I need to submit my poem to mysql database via hibernate. For this purpose I created a simple jsp page which has input fields for all Poem properties.
Upon submission RequestMethod.POST returns String values for title, body and author's name.
However that creates a type conversion error like: Failed to convert property value of type 'java.lang.String' to required type 'com.test.model.Author' for property 'author'.
Poem class expects Author object to be set into it but getting String name instead. I wonder why doesn't Spring make the necessary conversions since I explicitely create Author in my controller method? Shouldn't its values be automatically resolved and set after the page submission?
#RequestMapping(value = "/poem/add", method = RequestMethod.GET)
public ModelAndView addPoemPage() {
ModelAndView modelAndView = new ModelAndView("poem-add");
modelAndView.addObject("author", new Author());
modelAndView.addObject("poem", new Poem());
return modelAndView;
}
#RequestMapping(value = "/poem/add", method = RequestMethod.POST)
public ModelAndView addingPoem(#ModelAttribute Poem poem,
#ModelAttribute Author author) {
ModelAndView modelAndView = new ModelAndView("home");
authorService.addAuthor(author);
poem.setAuthor(author);
poemService.addPoem(poem);
return modelAndView;
}
My jsp page:
<%# page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%# taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<c:set var="url">${pageContext.request.requestURL}</c:set>
<link
href="${pageContext.request.contextPath}/resources/css/poem-add.css"
rel="stylesheet" />
<title>Writing a poem</title>
</head>
<body>
<h1>New poem</h1>
<p>Here you can write your poem.</p>
<form:form method="POST" commandName="poem"
action="${pageContext.request.contextPath}/poem/add.html">
<table>
<tbody>
<tr>
<td>Title:</td>
<td><input id="" title="Your poem's title" id="title"
name="title" type="text" class="input" /></td>
</tr>
<tr>
<td>Author:</td>
<td><input title="Author's name" id="author" name="author"
type="text" class="input" /></td>
</tr>
<tr>
<td>Date:</td>
<td><input title="Date of creation" id="date" name="date"
type="text" class="input" /></td>
</tr>
<tr>
<td>Text:</td>
<td><textarea title="Your poem goes here" rows="15" cols="50"
class="input"> </textarea></td>
</tr>
</tbody>
</table>
<table class="actions">
<tr>
<td><a
href="${pageContext.request.contextPath}/collection.html"><input
type="button" value="Back" class="button" /></a></td>
<td><a
href="${pageContext.request.contextPath}/collection.html"><input
type="submit" value="Submit" class="button" /></a></td>
</tr>
</table>
</form:form>
</body>
</html>
I know that I need a PropertyEditor or BeanWrapper. But I simply do not understand where and how do I implement them? What is the difference?
Summing my questions up, I need an explanation of what is going on "behind-the-scenes" between hibernate and SpringMVC right after I click submit button. If you could provide a sample of PropertyEditor or BeanWrapper for my case I would be endlessly grateful.
I need an explanation of what is going on "behind-the-scenes" between
hibernate and SpringMVC
: There is no hibernate yet. You are simply mapping a plain bean (model) to a JSP using Spring MVC.
I know that I need a PropertyEditor or BeanWrapper.
: You don't need a PropertyEditor at this stage. PropertyEditor is used for advanced type conversion like when you want an incoming date string "dd-mm-yyyy Zone" to be converted into a java.util.Date object and vice versa.
I wonder why doesn't Spring make the necessary
conversions since I explicitely create Author in my controller method?
Shouldn't its values be automatically resolved and set after the page
submission?
:Spring will automatically resolve if the JSP fields are mapped to the model attribute correctly using Spring Form tags. In your case, JSP form fields are not mapped to the model correctly and it should be as below
<form:form method="POST" modelAttribute="poem"
action="${pageContext.request.contextPath}/poem/add.html">
<table>
<tbody>
<tr>
<td>Title:</td>
<td><form:input path="poem.title" title="Your poem's title"
type="text" class="input" /></td>
</tr>
<tr>
<td>Author:</td>
<td><form:input path="poem.author.name" title="Author's name"
type="text" class="input" /></td>
</tr>
<tr>
<td>Text:</td>
<td><form:textarea path="poem.body" title="Your poem goes here" rows="15" cols="50"
class="input" /></td>
</tr>
</tbody>
</table>
<table class="actions">
<tr>
<td><a
href="${pageContext.request.contextPath}/collection.html"><input
type="button" value="Back" class="button" /></a></td>
<td><a
href="${pageContext.request.contextPath}/collection.html"><input
type="submit" value="Submit" class="button" /></a></td>
</tr>
</table>
</form:form>
You can add many attributes to the model but you can attach only one model attribute to the <form> and not two. Your controller would look like this.
#RequestMapping(value = "/poem/add", method = RequestMethod.GET)
public ModelAndView addPoemPage() {
ModelAndView modelAndView = new ModelAndView("poem-add");
Author author = new Author();
Poem poem = new Poem();
poem.setAuthor(author);
modelAndView.addObject("poem", new Poem());
return modelAndView;
}
#RequestMapping(value = "/poem/add", method = RequestMethod.POST)
public ModelAndView addingPoem(#ModelAttribute("poem") Poem poem) {
ModelAndView modelAndView = new ModelAndView("home");
authorService.addAuthor(poem.getAuthor);
poemService.addPoem(poem);
return modelAndView;
}