Spring mvc not able to receive a Set of string - spring

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

Related

Spring MVC sending Objects with checkbox

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;
}
...

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";
}

Values for th:field attributes in checkbox

I have table with datas from database (insert dynamically). In one column I insert checkbox. Now I want to select one of them and send to next form (I select one product and send properties to another form. In this form should be displayed properties only the select product). But I don't know what kind of value insert in th:field="*{}". I tried many solutions but doesn't work. My html form with all products table:
<form action="/oferta/zamow" th:action="#{/oferta/zamow}"
th:object="${oferta}" method="post">
<table border="1" id="display-data">
<tr>
<td>#</td>
<td>title</td>
<td>author</td>
<td>rok</td>
<td>cena</td>
<td></td>
</tr>
<tr th:each="produkt, pozycja : ${oferta}">
<td th:text="${pozycja.count}"></td>
<td><span th:text="${produkt.tytul}"></span></td>
<td><span th:text="${produkt.autor}"></span></td>
<td><span th:text="${produkt.rok}"></span></td>
<td><span th:text="${produkt.cena}"></span></td>
<td>
<input type="submit" value="zamow"/>
<!-- <a th:href="#{/zamowienie}">zamow</a> -->
</td>
<td>
<label>zamow</label>
<input type="checkbox" th:field="*{produkt}" th:value="${produkt}"/>
</td>
</tr>
</table>
</form>
Form to display select product:
<form action="/zamowienie/zam" th:action="#{/zamowienie/zam}"
th:object="${zamowienie}" method="post">
<table border="1" id="display-data">
<tr align="center">
<td colspan="2">twoje zamowienie</td>
</tr>
<tr>
<td>tytul</td>
<td><span th:text="${produkt.tytul}"></span></td>
</tr>
<tr>
<td>autor</td>
<td><span th:text="${produkt.autor}"></span></td>
</tr>
<tr>
<td>rok</td>
<td><span th:text="${produkt.rok}"></span></td>
</tr>
<tr>
<td>cena</td>
<td><span th:text="${produkt.cena}"></span></td>
</tr>
<tr>
<td>data zlozenia zamowienia</td>
<td><span th:text="${datazam}"></span></td>
</tr>
</table>
</form>
Thanks for help.
I am not sure if this is the answer you seek, but you can find an example at http://www.thymeleaf.org/doc/html/Thymeleaf-Spring3.html#checkbox-fields.
Here is a simple example to illustrate how to use a checkbox in Thymeleaf with Spring MVC.
Controller:
#RequestMapping(value = "/showForm", method=RequestMethod.GET)
public String showForm(Model model) {
List<String> allItems = new ArrayList<String>();
allItems.add("value1");
allItems.add("value2");
allItems.add("value3");
model.addAttribute("allItems", allItems);
Foo foo = new Foo();
List<String> checkedItems = new ArrayList<String>();
// value1 will be checked by default.
checkedItems.add("value1");
foo.setCheckedItems(checkedItems);
model.addAttribute("foo", foo);
...
}
#RequestMapping(value = "/processForm", method=RequestMethod.POST)
public String processForm(#ModelAttribute(value="foo") Foo foo) {
// Get value of checked item.
List<String> checkedItems = foo.getCheckedItems();
...
}
html:
<form action="#" th:action="#{/processForm}" th:object="${foo}" method="post">
<div th:each="item : ${allItems}">
<input type="checkbox" th:field="*{checkedItems}" th:value="${item}" />
<label th:text="${item}">example</label>
</div>
<input type="submit" />
</form>
Foo.java:
public class Foo {
private List<String> checkedItems;
public List<String> getCheckedItems() {
return checkedItems;
}
public void setCheckedItems(List<String> checkedItems) {
this.checkedItems = checkedItems;
}
}
Hope this helps.
Take a look at the thymeleaf spring integration docs.
All th:field are mapped against the command object. Thats why you need the *{} expression.
One thing the template engine is not able to do (yet) is mapping fields inside a loop directly. So you cannot use the *{} approach to reference the produkt variable from the loop.
What you have to do is use the index of the th:each expression and build a property accessor with a pre-evaluated expression for the index.
<input type="checkbox" th:field="*{produkts[__${index}__].checked" />
You do not need the th:value, th:field is taking care of it. (Except if you want to superseed it)

Resources