Spring boot maven mysql crud in single html page - spring

I created a spring boot crud application using a single HTML form. I want to display data in the text field for modification when I click the edit button. But print particular data in the console not appeared in the text field. I can not found what the problem.
Person Entity class
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
#Entity
#Table(name = "person")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "persion_id")
private int persondId;
#Column(name = "persion_name")
private String personName;
#Column(name = "persion_contact_no")
private String personContactNo;
#Column(name = "persion_address")
private String personAddress;
public Person() {
}
public Person(int persondId, String personName, String personContactNo, String personAddress) {
this.persondId = persondId;
this.personName = personName;
this.personContactNo = personContactNo;
this.personAddress = personAddress;
}
public int getPersondId() {
return persondId;
}
public void setPersondId(int persondId) {
this.persondId = persondId;
}
public String getPersonName() {
return personName;
}
public void setPersonName(String personName) {
this.personName = personName;
}
public String getPersonContactNo() {
return personContactNo;
}
public void setPersonContactNo(String personContactNo) {
this.personContactNo = personContactNo;
}
public String getPersonAddress() {
return personAddress;
}
public void setPersonAddress(String personAddress) {
this.personAddress = personAddress;
}
#Override
public String toString() {
return "Person [persondId=" + persondId + ", personName=" + personName + ", personContactNo=" + personContactNo
+ ", personAddress=" + personAddress + "]";
}
}
Controller Class
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.obydul.spring.model.Person;
import com.obydul.spring.service.PersonService;
#Controller
public class PersonController {
#Autowired
private PersonService personService;
#RequestMapping("/")
public String home() {
return "person";
}
#RequestMapping("/person")
public String showPerson(Model model) {
Person person = new Person();
model.addAttribute("person", person);
model.addAttribute("persons", personService.getAllPersons());
return "person";
}
#RequestMapping(value = "/person_save", method = RequestMethod.POST)
public String savePerson(#ModelAttribute Person person, Model model) {
personService.savePerson(person);
// model.addAttribute("persons", personService.getAllPersons());
return "redirect:/person";
}
#RequestMapping("/person_edit/{persondId}")
public String editPerson(#PathVariable int persondId, Model model) {
model.addAttribute("person", personService.getPersonById(persondId));
System.out.println("person edit :: " + personService.getPersonById(persondId));
return "redirect:/person";
}
#GetMapping("/persons/{persondId}")
public String deleteStudent(#PathVariable int persondId) {
personService.deletePersonById(persondId);
return "redirect:/person";
}
}
Person.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Person Information Page</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
crossorigin="anonymous">
</head>
<body>
<br>
<div class="container">
<div class="col-lg-10 col-md-8 col-sm-6 container justify-content-center card">
<h3 class="text-center">Person Info</h3>
<div class="card-body">
<form action="#" th:action="#{/person_save}" th:object="${person}"
method="post">
<div class="form-group">
<label>Person Name</label> <input type="text" name="personName"
th:field="*{personName}" class="form-control" />
</div>
<div class="form-group">
<label>Contact No</label> <input type="text"
name="personContactNo" th:field="*{personContactNo}"
class="form-control" />
</div>
<div class="form-group">
<label>Address</label> <input type="text" name="personAddress"
th:field="*{personAddress}" class="form-control" />
</div>
<div class="box-footer">
<button type="submit" class="btn btn-success">Save</button>
<button type="reset" class="btn btn-info">Reset</button>
</div>
<input type="hidden" id="persondId" th:field="*{persondId}"/>
<br>
<table class="table table-striped table-bordered">
<thead class="table-dark">
<tr>
<th class="text-center">Person Id</th>
<th class="text-center">Person Name</th>
<th class="text-center">Contact No</th>
<th class="text-center">Address</th>
<th class="text-center">Edit</th>
<th class="text-center">Delete</th>
</tr>
</thead>
<tbody>
<tr th:each="person: ${persons}">
<td class="text-center" th:text="${person.persondId}" />
<td th:text="${person.personName}" />
<td class="text-center" th:text="${person.personContactNo}" />
<td th:text="${person.personAddress}" />
<td class="text-center">
<a th:href="#{/person_edit/{persondId}(persondId=${person.persondId})}" class="btn btn-primary">Edit</a>
</td>
<td class="text-center">
<a th:href="#{/persons/{persondId}(persondId=${person.persondId})}" class="btn btn-danger">Delete</a>
</td>
</tr>
</tbody>
</table>
</form>
</div>
</div>
</div>
</body>
</html>
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.2</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.obydul.spring </groupId>
<artifactId>SpringBoot_Hibernate</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>SpringBoot_Hibernate</name>
<description>spring boot hibernate one to many relationship</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Person Info Screen
Console output
person edit :: Person [persondId=8, personName=John, personContactNo=021450000, personAddress=Dhaka]

replace this to line
#RequestMapping("/person_edit/{persondId}")
with this one:
#GetMapping("/person_edit/{persondId}")

Accoring to https://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html#inputs, you shouldn't be specifyin the name attribute on the form input elements. So instead of
<input type="text" name="personName" th:field="*{personName}" class="form-control" />
You should use
<input type="text" th:field="*{personName}" class="form-control" />
thymeleaf will populate the name attribute for you. I'm not sure if that's the reason why the value isn't being set though.

You've written person*d*Id not personId
<a th:href="#{/person_edit/{persondId}(persondId=${person.persondId})}" class="btn btn-primary">Edit</a>
try this instead:
<a th:href="#{/person_edit/{personId}(personId=${person.personId})}" class="btn btn-primary">Edit</a>
And it might work

It's work. I changed the editing part from the controller like this.
#GetMapping("/person_edit/{persondId}")
public String editPerson(#PathVariable int persondId, Model model) {
model.addAttribute("person", personService.getPersonById(persondId));
model.addAttribute("persons", personService.getAllPersons());
return "person";
}

Related

Input values were not transmitted to the PostMapping controller

create.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" type="text/css" href="/css/main.css" />
</head>
<body>
<a th:href="#{/admin/}">Admin</a>
<br>
<br>
<br>
<h1>Add core phrases to the database</h1>
<br>
<br>
<form method="POST" th:object="${corePhrasesCreateForm}">
<table>
<tr>
<td>Quotation cluster (" "):</td>
<td><input
type="number"
id="quotation-cluster"
name="quotation-cluster"
th:value="${quotationCluster}"/></td>
<td th:if="${#fields.hasErrors('quotationCluster')}" th:errors="*{quotationCluster}">quotationCluster</td>
</tr>
<tr>
<td>Quotation Exclamation cluster ("!")</td>
<td><input
type="number"
id="quotation-exclamation-cluster"
name="quotation-exclamation-cluster"
th:value="${quotationExclamationCluster}"/></td>
<td th:if="${#fields.hasErrors('quotationExclamationCluster')}" th:errors="*{quotationExclamationCluster}">quotationExclamationCluster</td>
</tr>
<tr>
<td>Phrases</td>
<td><textarea rows="5" cols="60" name="value" placeholder="Key phrases" th:text="${value}"></textarea></td>
<td class="error" th:if="${#fields.hasErrors('value')}" th:errors="*{value}">value</td>
</tr>
</table>
<br/>
<input type="submit" value="submit"/>
</form>
</body>
</html>
Validator
import com.example.marketing3.semantics.semanticsClusters.repositories.ClusterRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
#RequiredArgsConstructor
#Service
public class CorePhraseFormValidator implements Validator {
private final ClusterRepository clusterRepository;
#Override
public boolean supports(Class<?> clazz) {
return false;
}
#Override
public void validate(Object target, Errors errors) {
System.out.println();
}
Controller
#Controller
#RequestMapping("/semantics/phrases/core")
#RequiredArgsConstructor
public class CorePhraseController {
#GetMapping({"/create", "/create/"})
public String createGet(CorePhrasesCreateForm corePhrasesCreateForm) {
return "semantics/phrases/core/create";
}
#PostMapping({"/create", "/create/"})
public String createPost(#Valid CorePhrasesCreateForm corePhrasesCreateForm,
BindingResult bindingResult,
RedirectAttributes atts) {
corePhraseFormValidator.validate(corePhrasesCreateForm, bindingResult);
if (bindingResult.hasErrors()) {
return "semantics/phrases/core/create";
}
atts.addAttribute("message", "Core phrases created");
String result = "";
return "redirect:/general/message";
}
}
The form has been rendered.
I entered
Quotation cluster (" "): 1
Quotation Exclamation cluster ("!"): 2
And some text for phrases.
Like this:
The problem:
To createPost quotationCluster and quotationExclamationCluster came as zeroes.
But I entered 1 and 2 respectively.
Could you help me understand what I have don wrongly and correct the situation.
Change
<input type="number" id="quotation-cluster"
name="quotation-cluster" th:value="${quotationCluster}"/>
To:
<input type="number" id="quotation-cluster" th:field="*{questionCluster}"
name="quotationCluster" th:value="${quotationCluster}"/>
the name and th:field should be same as the declared attribute in your java class CorePhraseForm. Repeat this for the the next input (Textarea).

Spring boot thymeleaf (field.hasError always false)

I am building a web application and as part of my application, I am using Spring boot with Thymeleaf.
Spring version: 2.5.6, JDK: 17
When I try to submit form with some expected errors in input, the binding result identifies the errors and prints in the controller but in Thymeleaf when I check for errors nothing shows up.
POM.XML
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.6</version>
<relativePath />
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
Application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/unknown
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.testWhileIdle=true
spring.datasource.validationQuery=SELECT 1
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.sql.init.mode=never
spring.thymeleaf.cache=false
spring.thymeleaf.check-template=true
spring.thymeleaf.check-template-location=true
spring.thymeleaf.enabled=true
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
logging.level.org.springframework=INFO
logging.level.org.thymeleaf=ERROR
logging.level.org.springframework.boot=ERROR
HMTL with Thymeleaf:
<form class="row mb-0" action="#" th:action="#{/post}" th:object="${formData}" method="post">
<div class="col-md-6 form-group">
<label for="name">Name <small>*</small></label>
<label th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="validation-message"></label>
<input type="text" id="name" th:field="*{name}" class="sm-form-control" />
</div>
<div class="col-md-6 form-group">
<label for="email">Email <small>*</small></label>
<label th:if="${#fields.hasErrors('email')}" th:errors="*{email}" class="validation-message"></label>
<input type="email" id="email" th:field="*{email}" class="sm-form-control" />
</div>
<div class="w-100"></div>
<div class="col-md-6 form-group">
<label for="phone">Phone</label>
<label th:if="${#fields.hasErrors('phone')}" th:errors="*{phone}" class="validation-message"></label>
<input type="text" id="phone" th:field="*{phone}" class="sm-form-control" />
</div>
<div class="col-md-6 form-group">
<label for="service">Services</label>
<label th:if="${#fields.hasErrors('service')}" th:errors="*{service}" class="validation-message"></label>
<select id="service" class="sm-form-control" th:field="*{service}" >
<option value="">-- Select One --</option>
<option th:each="service : ${T(com.utterlydesi.web.constant.Service).values()}"
th:value="${service}"
th:text="${service}">service
</option>
</select>
</div>
<div class="w-100"></div>
<div class="col-12 form-group">
<label for="title">Title <small>*</small></label>
<div class="alert alert-warning" th:if="${#fields.hasErrors('title')}" th:errors="*{title}"></div>
<input type="text" id="title" th:field="*{title}" class="sm-form-control" />
</div>
<div class="col-12 form-group">
<label for="description">Description <small>*</small></label>
<div class="alert alert-warning" th:if="${#fields.hasErrors('description')}" th:errors="*{description}"></div>
<textarea class="required sm-form-control" id="description" name="template-contactform-message" rows="6" cols="30" th:field="*{description}"></textarea>
</div>
<div class="col-12 form-group">
<button class="button button-3d m-0" type="submit" id="add-post-submit" value="submit" th:text="Submit"></button>
</div>
<div th:if="${result != null}">
<span class="alert alert-primary" role="alert" th:utext="${result}"></span>
</div>
<ul th:if="${#fields.hasErrors('*')}">
<li th:each="error : ${#fields.errors('*')}" th:text="${error}">error</li>
</ul>
<ul th:if="${#fields.hasErrors('global')}">
<li th:each="error : ${#fields.errors('global')}" th:text="${error}">error</li>
</ul>
</form>
Controller:
#Controller
public class PostController {
#GetMapping(value = {"/post"})
public String post(Model model) {
model.addAttribute("formData", new PostAdd());
return "post";
}
#PostMapping("/post")
public String addPost(#ModelAttribute("formData") #Valid PostAdd formData, BindingResult bindingResult, Model model) {
try {
System.out.println(formData.toString());
if (bindingResult.hasErrors()) {
System.out.println("2");
System.out.println(bindingResult.hasErrors() + " " + bindingResult.hasFieldErrors("description"));
System.out.println(model.asMap());
model.addAttribute("result", "There was an issue with registration. Please corrected the fields highlighted in red");
return "post";
} else {
System.out.println("3");
//model.addAttribute(POST_ADD_OBJ_KEY, new PostAdd());
//model.addAttribute("result", "Post added successfully");
}
System.out.println(bindingResult.hasErrors() + " " + bindingResult.hasFieldErrors("description"));
return "redirect:/index";
} catch (Exception e) {
e.printStackTrace();
return "redirect:/index";
}
}
public PostController() {
}
}
PostAdd.java
#Data
public class PostAdd {
#NotEmpty(message = "*Please provide a name")
private String name;
#Email(message = "*Please provide a valid Email")
#NotEmpty(message = "*Please provide an email")
private String email;
#Length(min = 10, message = "*Your phone number must have at least 10 characters")
#NotEmpty(message = "*Please provide a valid phone number")
private String phone;
#NotNull(message = "*Please select a valid service")
private Service service;
#Length(min = 5, message = "*Please add a more descriptive title")
#NotEmpty(message = "*Please provide a title")
private String title;
#Length(min = 100, message = "*Please make your description more descriptive. At least 100 characters is required")
#NotEmpty(message = "*Please enter description")
private String description;
}
SOP output from controller:
PostAdd(name=First Middle Last, email=fml#gmail.com, phone=11111111111, service=null, title=11, description=111)
2
true true
{formData=PostAdd(name=First Middle Last, email=fml#gmail.com, phone=10123456789, service=null, title=11, description=111), org.springframework.validation.BindingResult.formData=org.springframework.validation.BeanPropertyBindingResult: 3 errors
Field error in object 'formData' on field 'description': rejected value [111]; codes [Length.formData.description,Length.description,Length.java.lang.String,Length]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [formData.description,description]; arguments []; default message [description],2147483647,100]; default message [*Please make your description more descriptive. At least 100 characters is required]
Field error in object 'formData' on field 'service': rejected value [null]; codes [NotNull.formData.service,NotNull.service,NotNull.com.utterlydesi.web.constant.Service,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [formData.service,service]; arguments []; default message [service]]; default message [*Please select a valid service]
Field error in object 'formData' on field 'title': rejected value [11]; codes [Length.formData.title,Length.title,Length.java.lang.String,Length]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [formData.title,title]; arguments []; default message [title],2147483647,5]; default message [*Please add a more descriptive title]}
What I have tried so far based on some other posts I have seen on Stackoverflow?
Downgrade to JDK 8
Downgrade to Spring boot less than 2.3.0
Add #EnableWebMVC
Replaced #PostMapping with #RequestMapping
Switched places of #Valid with #ModelAttribute

Can't pass an object from one Thymeleaf view to another Thymeleaf view

I'm new to Spring, and I'm working on a Spring MVC + Thymeleaf application that tracks personal expenses. I have a page that shows the list of expenses, and upon clicking "Update" on any given expense, it would show a pre-populated form, on another page, with that expense's information. The problem I'm having is that I want to pass that expense object forward to this second page, but since the data was already fetched from the database in my first page, I wouldn't want to fetch it again from my Spring Data JPA repository.
I think the unusual part of my objective is that I'm trying to pass the object like this:
Controller -> Thymeleaf -> Controller -> Thymeleaf
While the controller has sent an Expenses object to the list of expenses page, I'm trying to resend that same object (or just the one Expense, even better) back to the controller, so it would populate the model in my second form page.
My Expense entity:
package com.williampoletto.expensetracker.entity;
import java.time.LocalDate;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#EqualsAndHashCode(of= "id")
#ToString(exclude="categories")
#Entity
#Table
public class Expense {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY )
private int id;
#NotBlank
private String description;
#NotNull
private double value;
#NotNull
private LocalDate date;
private String note;
#ManyToOne
private User user;
#ManyToMany(cascade=CascadeType.PERSIST)
#JoinTable(
name="expense_category",
joinColumns=#JoinColumn(name="expense_id"),
inverseJoinColumns=#JoinColumn(name="category_id"))
private Set<Category> categories;
public void addCategory(Category category) {
categories.add(category);
}
public void removeCategory(Category category) {
categories.remove(category);
}
public Expense(int id, #NotBlank String description, #NotNull double value, #NotNull LocalDate date,
String note, Set<Category> categories) {
super();
this.id = id;
this.description = description;
this.value = value;
this.date = date;
this.note = note;
this.categories = categories;
}
}
My thymeleaf table:
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Description</th>
<th>Value</th>
<th>Category</th>
<th>Note</th>
<th>Date</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr th:each="expense : ${expenses}">
<td th:text="${expense.description}"/>
<td th:text="${expense.value}"/>
<td>
<div th:each="category : ${expense.categories}">
<p th:text ="${category.name}"></p>
</div>
</td>
<td th:text="${expense.note}"/>
<td th:text="${expense.date}"/>
<td>
<form th:action="#{/expenses/showUpdateForm}" th:object="${expense}" method="POST">
<input type="text" name="expense" th:value="${expense.id}">
<input type="text" name="expenseDescription" th:value="${expense.description}">
<input type="text" name="expenseValue" th:value="${expense.value}">
<input type="text" name="expenseDate" th:value="${expense.date}">
<input type="text" name="expenseNote" th:value="${expense.note}">
<input type="text" name="expenseCategories" th:value="${expense.categories}">
<button th:if="${expense.user != null}" type="submit" class="btn btn-info btn-sm">Update</button>
</form>
<a th:href="#{/expenses/delete(expenseId=${expense.id})}"
class="btn btn-danger btn-sm"
onclick="if (!(confirm('Are you sure you want to delete this expense?'))) return false">Delete</a>
</td>
</tbody>
</table>
My /list and /showUpdateForm code for the controller:
#GetMapping("/list")
public String list(#AuthenticationPrincipal UserDetailsImpl userDetails, Model model) {
Set<Expense> expenses = expenseService.findAll(userDetails.getUserId());
model.addAttribute("expenses", expenses);
return "expenses";
}
#PostMapping("/showUpdateForm")
public String showFormForUpdate(#RequestParam("expenseId") int expenseId,
#RequestParam("expenseDescription") String expenseDescription,
#RequestParam("expenseValue") double expenseValue,
#RequestParam("expenseDate") String expenseDate,
#RequestParam("expenseNote") String expenseNote,
#RequestParam("expenseCategories") Set<Category> expenseCategories,
Model model) {
Expense expense = new Expense
(expenseId, expenseDescription, expenseValue, expenseDate, expenseNote, expenseCategories);
model.addAttribute("expense", expense);
return "/expense-form";
}
My final form page:
<form th:action="#{/expenses/save}" th:object="${expense}" method="POST">
<input type="hidden" th:field="*{id}">
<label>Description</label>
<input type="text" th:field="*{description}" class="form-control mb-4 col-4" placeholder="Description">
<label>Value</label>
<input type="text" th:field="*{value}" class="form-control mb-4 col-4" placeholder="Value">
<label>Date</label>
<input type="text" th:field="*{date}" class="form-control mb-4 col-4" placeholder="Date">
<label>Note</label>
<input type="text" th:field="*{note}" class="form-control mb-4 col-4" placeholder="Note">
<label>Categories</label>
<input type="hidden"
th:each="category : *{categories}"
th:value="${category.id}"
th:field="*{categories}"
th:text="${category.name}"/>
<button type="submit" class="btn btn-success col-2 mb-2">Save</button>
</form>
What I have tried:
Instead of sending an object, sending individual values as you can see in my thymeleaf table. The problem with this is that my Expense entity has a LocalDate, User (another entity) and Set attributes, so I had trouble converting these to an Expense object on the controller side. Ideally I would want to write something like this to simply pass on the object, but this example sends a toString:
<form th:action="#{/expenses/showUpdateForm}" th:object="${expense}" method="POST">
<input type="text" name="expense" th:value="${expense}">
</form>
Tried to use an argument to /showUpdateForm to hopefully get the expenses object from the model:
#RequestAttribute("expenses") Set<Expense> expenses
Tried to retrieve the object like this in /showUpdateForm, with the same intent as 2:
Set<Expense> expenses = (Set<Expense>) model.getAttribute("expenses");
Tried to use RedirectAttributes in the controller, which I saw can be useful for passing objects between controllers, but maybe not in my case:
#GetMapping("/showUpdateForm")
public String showFormForUpdate(Model model, RedirectAttributes attributes) {
attributes.addAttribute("expenses");
return "/expense-form";
}
Anyway, I have no idea how to achieve this, I would appreciate any light on the subject! I know that I can easily fix this by simply sending an id from view to controller, then I could perform a repository search of the object with that ID, but that would be cumbersome to the database since the data already exists and was fetched in the previous page.

Unable to Run the following Spring Boot Application that uses JDBC Template

I have created a simple Spring Boot Application that adds Dog information into the MySql database.
The controller class for this Application is DogController.java
package com.dog.resue.controller;
import com.dog.resue.dao.DodRepository;
import com.dog.resue.service.DogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Date;
#Controller
#RequestMapping(path="/")
public class DogController {
#Autowired
private DodRepository dodRepository;
#Autowired
private DogService dogService;
#RequestMapping(value ="/home",method = {RequestMethod.POST,RequestMethod.GET})
public String adddog(#RequestParam("name") String name,
#RequestParam("rescued") #DateTimeFormat(pattern = "yyyy-MM-dd") Date rescued,
#RequestParam("vaccinated") Boolean vaccinated, Model model)
{
dogService.addADog(name, rescued, vaccinated);
System.out.println("name = " + name + ",rescued = " + rescued + ", vaccinated = " + vaccinated);
return "index";
}
}
and the corresponding Service class is
package com.dog.resue.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import javax.sql.DataSource;
import java.util.Date;
#Service
public class DogServiceImpl implements DogService {
#Autowired
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
this.jdbcTemplate=new JdbcTemplate(dataSource);
}
#Override
public void addADog(String name, Date rescued, Boolean vaccinated) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.update("INSERT INTO dog(name,rescued,vaccinated) VALUES(?,?,?)",name,rescued,vaccinated );
}
}
And the thymeleaf HTML File is
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<!-- META SECTION -->
<title>Dog Rescue</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- END META SECTION -->
<!-- BEGIN STYLE -->
<style>
table, th, td {
border: 1px solid black;
padding: 1px;
}
</style>
<!-- END STYLE -->
</head>
<body>
<h2>Current Dogs In Rescue</h2>
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Rescue Date</th>
<th>Vaccinated</th>
</tr>
</thead>
<tbody>
<tr th:each="dogs : ${dogs}">
<td th:text="${dogs.id}">Text ...</td>
<td th:text="${dogs.name}">Text ...</td>
<td th:text="${dogs.rescued}">Text ...</td>
<td th:text="${dogs.vaccinated}">Text...</td>
</tr>
</tbody>
</table>
</div>
<h2>Add A Dog</h2>
<form action="#" th:action="#{/home}" >
<label>Name<input type="text" name="name" id="name"></input></label>
<label>Vaccinated<input type="text" name="vaccinated" id="vaccinated"></input></label>
<label>Rescued<input type="text" name="rescued" id="rescued"></input></label>
<input type="submit" value="Submit"></input>
</form>
</body>
</html>
While running this code i am getting following Error
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Sun Jul 22 21:50:32 IST 2018
There was an unexpected error (type=Bad Request, status=400).
Required String parameter 'name' is not present
url is http://localhost:8080/home
Kindly help me to solve this issues
Your Request parameters are missing in URL( name,rescued,vaccinated)
Your url should be
http://localhost:8080/home?name=ARULSUJU&rescued=2012-12-12&vaccinated=true
because all the parameters are required
Looking at your controller
Why you have rescued as Date Type you can change it as String.
So your controller will be
#RequestMapping(value ="/home",method = {RequestMethod.POST,RequestMethod.GET})
public String adddog(#RequestParam("name") String name,
#RequestParam("rescued") String rescued,
#RequestParam("vaccinated") Boolean vaccinated, Model model)
{
dogService.addADog(name, rescued, vaccinated);
System.out.println("name = " + name + ",rescued = " + rescued + ", vaccinated = " + vaccinated);
return "index";
}
Now try this URL
http://localhost:8080/home?name=test&rescued=2014-12-12&vaccinated=true
in your thymeleaf html file add this xmlns :
xmlns:th="http://www.thymeleaf.org"

Thymeleaf form validation with spring MVC

This has been asked a few times but all of them did not answer my question. I have been trying to get different functionalities to work with Thymeleaf for two days now and been very unsuccessful. I have only been able to get it to work with spring-boot, however right now I am using spring-MVC.
First I will show you my dependencies
<%# taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
<%# page contentType="text/html;charset=UTF-8" language="java" %>
<html xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Create a New Account</title>
<link th:href="#{/resources/css/loginForm.css}" href="/resources/css/loginForm.css" rel="stylesheet"
type="text/css"/>
</head>
<body>
<form action="#" th:action="#{/createNewAccount}" th:object="${user}" method="post">
<table>
<tr>
<td>Name:</td>
<td><input type="text" th:field="*{username}" /></td>
<td th:if="${#fields.hasErrors('name')}" th:errors="*{username}">Name Error</td>
</tr>
<tr>
<td>Password:</td>
<td><input type="text" th:field="*{password}" /></td>
<td th:if="${#fields.hasErrors('age')}" th:errors="*{password}">Password Error</td>
</tr>
<tr>
<td><button type="submit">Submit</button></td>
</tr>
</table>
</form>
</body>
</html>
Now so you can see the errors my intellij IDE is showing:
User.java
package com.practice.domain;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
* Created by DrewJocham on 8/30/15.
*/
public class User {
#NotNull
#Size(min = 2, max = 30)
private String username;
#NotNull
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
The field name you call hasErrors with needs to match the field name in the object. Like so:
<td th:if="${#fields.hasErrors('username')}" th:errors="*{username}">Name Error</td>
Note that hasErrors('name') became hasErrors('username'), and:
<td th:if="${#fields.hasErrors('password')}" th:errors="*{password}">Password Error</td>
Note that hasErrors('age') became hasErrors('password').
As for the errors being highlighted in Intellij, I think they're misleading, and are related to this open issue: https://youtrack.jetbrains.com/issue/IDEA-132738

Resources