Spring MVC sending Objects with checkbox - spring

I need a help sending object to my list which is part of the object Rent(List), which contains all the Copies which i have to rent and which are selected with the checkbox in the table with. However, when i try to do this, copies list in rent is always empty. How can i solve this problem? Selecting client and dates perfectly works, but getting the List not.
JSP view
Jsp page
...
<form:form action="${pageContext.request.contextPath}/rent/save" enctype="multipart/form-data" method="post" modelAttribute="rentObject">
<div>Select client:</div>
<form:select path="client.clientID">
<div class="container-fluid">
<c:forEach
items="${clients}"
var="p"
varStatus="loop">
<form:option value="${p.clientID}">${p.name} ${p.surname},ID: ${p.clientID}</form:option>
</c:forEach>
</form:select>
<div>Date from:</div>
<div><form:input type="date" id="dateFrom" path="dateFrom"/></div>
<div class="text-danger">
<form:errors path="dateFrom" cssClass="error" />
</div>
<div>Date to:</div>
<div><form:input type="date" id="dateTo" path="dateTo" /></div>
<div class="text-danger">
<form:errors path="dateTo" cssClass="error" />
</div>
<div><button id="save" class="btn btn-primary">Save</button> </div>
<p/>
</div>
<div class="col-sm-7 text-left">
<h2>Copies</h2>
<table id="tabela" class="table table-striped table-bordered table-sm" cellspacing="0" width="100%">
<thead>
<tr>
<th scope="col">Copy ID</th>
<th scope="col">Equipment name</th>
<th scope="col">Equipment picture</th>
<th scope="col">Defect</th>
<th scope="col">Rent</th>
</tr>
</thead>
<tbody>
<c:forEach items="${copiesAll}" var="m" varStatus="loop">
<tr>
<td>${m.copyID}</td>
<td>${m.equipment.name}</td>
<td>
Show picture
</td>
<td>${m.defect}</td>
<td align="center">
<form:checkbox path="copies" value="${m}"/>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</form:form> ```
Rent class
#Entity
#Table(name = "rent")
public class Rent implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "rentID")
private int rentID;
#DateTimeFormat(pattern = "yyyy-MM-dd")
#Temporal(javax.persistence.TemporalType.DATE)
private Date dateFrom;
#DateTimeFormat(pattern = "yyyy-MM-dd")
#Temporal(javax.persistence.TemporalType.DATE)
private Date dateTo;
#ManyToOne(cascade = CascadeType.MERGE)
#JoinColumn(name = "clientid")
private Client client;
#OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.DETACH, CascadeType.PERSIST})
#JoinColumn(name = "rentid", referencedColumnName = "rentID")
private List<Copy> copies;
#ManyToOne(cascade = CascadeType.MERGE)
#JoinColumn(name = "userID", referencedColumnName = "username")
private Worker worker;
public Rent() {
this.copies = new ArrayList<>();
}
...
Copy class
#Entity
#Table(name = "copy")
public class Copy implements Serializable {
#ManyToOne(cascade = {CascadeType.DETACH,CascadeType.MERGE},fetch = FetchType.EAGER)
#JoinColumn(name = "equipmentid")
private Equipment equipment;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "copyID")
private Integer copyID;
private boolean working;
private boolean available;
private String defect;
private Integer rentid;
public Copy() {
working = true;
available = true;
defect = null;
}
...

Related

Spring mvc not able to receive a Set of string

hibernate
I have an entity: Factories
#Entity
public class Factories extends BaseEntity {
#Id
#SequenceGenerator(name = "factories_id_seq", sequenceName = "factories_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "factories_id_seq")
private Integer id;
#ElementCollection
private Set<String> emails = new HashSet<String>();
//get set...
}
In thymeleaf to add email to factories,
<table id="emailsTable" class="table table-striped table-hover responsive">
<thead>
<tr>
<th th:text="#{value}">Value</th>
<th></th>
</tr>
</thead>
<tbody>
<tr th:each="email, stat : *{emails}">
<td><input type="text" class="form-control" th:placeholder="#{email.placeholder}" placeholder="Name" th:field="*{emails[__${stat.index}__]}" /></td>
<td class="align-middle"><button type="button" class="btn btn-default pull-right delete"><i class="fas fa-trash-alt"></i></button></td>
</tr>
</tbody>
</table>
When I try to save, I get
org.springframework.beans.InvalidPropertyException: Invalid property
'emails[0]' of bean class [com.lcm.model.Factories]: Property
referenced in indexed property path 'emails[0]' is neither an array
nor a List nor a Map; returned value was [[]]
it's not possible because set have no order...check this post there are a few alternative...
Need help with binding Set with Spring MVC form

Can I use duplicate each loop by Thymeleaf?

I have a headache because of Thymeleaf.
I want to handle complex object in Thymeleaf like loop in loop.
See this code:
thymeleaf code
<tr th:each="wrapper:${logWrappers}">
<td th:text="${wrapper.serverName}">
<tr th:each="log:${wrapper.logs}">
<tr th:text="${log.name}">
<tr th:each="debug:${log.debug}" th:text="${debug}"></tr>
</tr>
</tr>
</td>
</tr>
model
public class LogWrapper {
private String serverName;
private List logs;
// getters and setters
}
object in model
public class LogModule {
private String name;
private String logType;
private List<String> debugs = new ArrayList<>();
private List<String> infos = new ArrayList<>();
private List<String> errors = new ArrayList<>();
// getters and setters
}
send parameter
model.addAttribute("logWrappers", logWrappers);
problem
<tr th:each="log:${wrapper.logs}">
This code is not working. How can i solve it?
retry
<tr> <td> <tr th:each="wrapper:${logWrappers}">
<td th:text="${wrapper.serverName}">
<tr th:each="log:${wrapper.logs}"> <td th:text="log.name">
<tr th:each="debug:${log.debug}"> <td th:text="${debug}"></td>
</tr>
</td>
</tr>
</td>
</tr>
</td>
</tr>

how to use javax.validation with Thymeleaf

When I run this it will create a new book if valid however upon error it will not post back to the page and I get a 400:
This application has no explicit mapping for /error, so you are seeing this as a fallback.
There was an unexpected error (type=Bad Request, status=400).
Validation failed for object='book'. Error count: 4
I tested with system outs, it never gets to the submit if it has errors, I think maybe #Valid is wrong or ???
This is my controller... please tell me what's missing ?
#Controller
#RequestMapping("/")
public class BookController{
#RequestMapping(method=RequestMethod.GET)
public String home(Model model) {
refData = new ReferenceData();
model.addAttribute("refData", refData);
model.addAttribute("book", new Book());
List<Book> books = BookRepo.findAll();
model.addAttribute("books", books);
return "home";
}
#RequestMapping(method=RequestMethod.POST)
public String submit(#Valid Book book,Model model, BindingResult bindingResult) {
refData = new ReferenceData();
model.addAttribute("refData", refData);
if (bindingResult.hasErrors()) {
return "home";
}
BookRepo.save(book);
return "redirect:/";
}
My form looks like this...
<form name="addForm" action="#" th:action="#{/}" th:object="${book}" method="post">
<table>
<tr>
<td><label for="*{title}">Book:</label></td>
<td><input type="text" th:field="*{title}" /></td>
<td th:if="${#fields.hasErrors('title')}" th:errors="*{title}">Name
Error</td>
</tr>
<tr>
<td><label for="*{author}">Author:</label></td>
<td><input type="text" th:field="*{author}" /></td>
<td th:if="${#fields.hasErrors('author')}" th:errors="*{author}">Author
Error</td>
</tr>
<tr>
<td><label for="*{genre}">Genre:</label></td>
<td><input type="text" th:field="*{genre}" /></td>
<td th:if="${#fields.hasErrors('genre')}" th:errors="*{genre}">genre
Error</td>
</tr>
<tr>
<td><label for="*{pages}">Pages:</label></td>
<td><input type="text" th:field="*{pages}" /></td>
<td th:if="${#fields.hasErrors('pages')}" th:errors="*{pages}">pages
Error</td>
</tr>
<tr>
<td><label th:for="*{year}">Year:</label></td>
<td>
<select th:field="*{year}" ng-model="bookData.year">
<option th:each="selectItem: ${refData.years}"
th:value="${selectItem.value}" th:text="${selectItem.label}">year</option>
</select>
</td>
<td th:if="${#fields.hasErrors('year')}" th:errors="*{year}">year
Error</td>
</tr>
<tr>
<td><label th:for="*{rating}">Rating:</label></td>
<td><select th:field="*{rating}" ng-model="bookData.rating">
<option th:each="selectItem: ${refData.rating}"
th:value="${selectItem.value}" th:text="${selectItem.label}">rating</option>
</select></td>
<td th:if="${#fields.hasErrors('rating')}" th:errors="*{rating}">rating
Error</td>
</tr>
<tr>
<td><button type="submit">Submit</button></td>
</tr>
</table>
</form>
my entity...
#Entity
public final class Book
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#NotNull
#Pattern(regexp="^[a-zA-Z0-9_]+$")
private String title;
#NotNull
#Pattern(regexp="^[a-zA-Z0-9_]+$")
private String author;
#NotNull
#Pattern(regexp="^[a-zA-Z0-9_]+$")
private String genre;
#NotNull
#Digits(message="message here", integer=32767, fraction = 0)
private Integer pages;
private String year;
private String rating;
I found I had to place the bindingResult directly after the #Valid Book entity thus...
#RequestMapping(method=RequestMethod.POST)
public String submit(#Valid Book book,**BindingResult bindingResult**, Model model ) {
refData = new ReferenceData();
model.addAttribute("refData", refData);
if (bindingResult.hasErrors()) {
return "home";
}
BookRepo.save(book);
return "redirect:/";
}
Wow what a minor=major mistake !!
Johnny O.

The request sent by the client was syntactically incorrect. <form:option

I am getting "The request sent by the client was syntactically incorrect." while persisting Visitor Object. If I remove the
VisitorDetails.java
#Entity
#Table(name = "visitor_details")
public class VisitorDetails {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
private String mobileNumber;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "society_id")
private SocietyDetails societyDetails;
public SocietyDetails getSocietyDetails() {
return societyDetails;
}
SocietyDetails.java
#Entity
#Table(name = "society_details")
public class SocietyDetails {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(name = "society_name")
private String societyName;
#Column(name = "society_address")
private String societyAddress;
visitor.jsp
<form:form action="${pageContext.request.contextPath}/visitor/add"
commandName="visitor">
<table>
<tr>
<td>First Name:</td>
<td><form:input path="firstName" /></td>
</tr>
<tr>
<td>Last Name:</td>
<td><form:input path="lastName" /></td>
</tr>
<tr>
<td>Mobile Number:</td>
<td><form:input path="mobileNumber" /></td>
</tr>
<tr>
<td>Society:</td>
<td><form:select path="societyDetails">
<form:option value="" label="Select Society" />
<form:options items="${societyDetailsLst}"
itemLabel="societyName" itemValue="id" />
</form:select></td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="Save Changes" />
</td>
</tr>
</table>
</form:form>
Controller class - VisitorController.java
#RequestMapping(value = "/visitor/add", method = RequestMethod.POST)
public String addVisitor(#ModelAttribute("visitor") VisitorDetails visitorDet) {
visitorService.create(visitorDet);
return "visitor";
}

Adding custom error messages for validation in Spring-MVC

I am using Spring-MVC and I would like to do validation tests in backend. Currently I am able to perform the tests, redirect properly to the link. But I am only unable to add custom error messages incase there is a problem, Example : "Firstname is empty". I tried using the ValidationMessages_en_EN.properties file in WEB-INF, but that also didn't help. I am posting my code, kindly let me know where I am going wrong :
COntroller :
#Controller
public class PersonController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public String listPersons(Model model) {
Person person = personService.getCurrentlyAuthenticatedUser();
if(!(person==null)){
return "redirect:/canvas/list";
} else {
model.addAttribute("person", new Person());
// model.addAttribute("listPersons", this.personService.listPersons());
model.addAttribute("notices",new Notes());
model.addAttribute("canvases",new Canvas());
return "person";
}
}
#RequestMapping(value= "/person/add", method = RequestMethod.POST)
public String addPerson(#Valid Person person,BindingResult bindingResult,#ModelAttribute("person") Person p,Model model){
if(bindingResult.hasErrors()){
return "redirect:/";
}
this.personService.addPerson(p);
return "redirect:/";
}
Entity :
#Entity
#Table(name="person")
public class Person implements UserDetails{
private static final GrantedAuthority USER_AUTH = new SimpleGrantedAuthority("ROLE_USER");
#Id
#Column(name="id")
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "person_seq_gen")
#SequenceGenerator(name = "person_seq_gen",sequenceName = "person_seq")
private int id;
#Valid
#NotEmpty #Email(message = "username.empty")
#Column(name = "username")
private String username;
#NotEmpty
#Column(name = "password")
private String password;
#Valid
#Size(min = 2,max = 30,message = "firstName.empty")
#Column(name = "firstname")
private String firstName;
#Valid
#Size(min = 2,max = 50)
#Column(name = "secretquestion")
private String secretquestion;
#Valid
#Size(min = 2,max = 500,message = "secretanswer.empty")
#Column(name = "secretanswer")
private String secretanswer;
Servlet-Context.xml
<beans:bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<beans:property name="basename" value="ValidatorMessages_en_EN.properties"/>
</beans:bean>
JSP/HTML :
<c:url var="addAction" value="/person/add" ></c:url>
<form:form action="${addAction}" commandName="person" method="post">
<table>
<c:if test="${!empty person.username}">
<tr>
<td>
<form:label path="id">
<spring:message text="ID"/>
</form:label>
</td>
<td>
<form:input path="id" readonly="true" size="8" disabled="true" />
<form:hidden path="id" />
</td>
</tr>
</c:if>
<tr>
<td>
<form:label path="firstName">
<spring:message text="FirstName"/>
</form:label>
</td>
<td>
<form:input path="firstName" />
</td>
<td><form:errors path="firstName"/>
</tr>
<tr>
<td>
<form:label path="username">
<spring:message text="Email"/>
</form:label>
</td>
<td>
<form:input path="username" />
</td>
<td><form:errors path="username"/>
</tr>
<tr>
<td>
<form:label path="password">
<spring:message text="Password"/>
</form:label>
</td>
<td>
<form:input path="password" />
</td>
<td><form:errors path="Password"/>
</tr>
<tr>
<td>
<form:label path="secretquestion">
<spring:message text="secretquestion"/>
</form:label>
</td>
<td>
<form:input path="secretquestion" />
</td>
<td><form:errors path="secretquestion"/>
</tr>
<tr>
<td>
<form:label path="secretanswer">
<spring:message text="secretanswer"/>
</form:label>
</td>
<td>
<form:input path="secretanswer" />
</td>
<td><form:errors path="secretanswer"/>
</tr>
POM.xml :
<!-- Validation -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.3.1.Final</version>
</dependency>
Messages.properties :
firstName=firstName
Email=Email
Password=password
secretquestion=secretquestion
secretanswer=secretanswer
NotEmpty.person.firstName=firstName is required!
NotEmpty.person.username=Email is required!
NotEmpty.person.password=Password is required!
NotEmpty.person.secretquestion=SecretQuestion is required!
NotEmpty.person.secretanswer=SecretAnswer is required!
Messages_en_US.properties
firstName=firstName
Email=Email
Password=password
secretquestion=secretquestion
secretanswer=secretanswer
NotEmpty.person.firstName=firstName is required!
NotEmpty.person.username=Email is required!
NotEmpty.person.password=Password is required!
NotEmpty.person.secretquestion=SecretQuestion is required!
NotEmpty.person.secretanswer=SecretAnswer is required!
<spring:message text="FirstName"/> Should be like <spring:message code="FirstName"/> try to replace text to code in your <spring:message /> tag
and change basename value to ValidatorMessages and en_US Spring will append for you
<beans:bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<beans:property name="basename" value="ValidatorMessages"/>
</beans:bean>
sava your file as ValidatorMessages_en_US.properties in your resource folder
#ExceptionHandler(Exception.class)
#ResponseBody
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public Response handleException(Exception e) {
Response response = new Response();
if(e instanceof MethodArgumentNotValidException ){
MethodArgumentNotValidException methodException = (MethodArgumentNotValidException)e;
BindingResult bindingResult =methodException.getBindingResult();
if(bindingResult!=null && bindingResult.hasErrors()){
List<FieldError> fieldErrorList= bindingResult.getFieldErrors();
for(FieldError fieldError: fieldErrorList ){
response.adderror(fieldError.getDefaultMessage());
}
}
}
return response;
}

Resources