Save input field in Thymeleaf to Set<> - java

I would like to save the inputs to the Set interface. I have class Client.java:
#Table(name = "client")
public class Client {
#OneToOne(cascade = CascadeType.ALL)
private ShippingAddress shippingAddress;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Address> shippingAddresses = new HashSet<>();
}
Class ShippingAddress.java:
#Table(name = "address")
public class ShippingAddress {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
Long id;
String street;
String zip;
}
And it's my create form:
<form th:action="#{/add}" method="post" th:object="${client}">
<tr>
<td><input class="form-control" type="text" th:field="*{shippingAddress.street}"/></td>
<td><input class="form-control" type="text" th:field="*{shippingAddress.zip}"/></td>
</tr>
It works fine, but I can only save one street and one zip. I tried to improve it in this way to be able to save more data:
<form th:action="#{/add}" method="post" th:object="${client}">
<tr>
<td><input class="form-control" type="text" th:field="*{shippingAddress[0].street}"/></td>
<td><input class="form-control" type="text" th:field="*{shippingAddress[0].zip}"/></td>
</tr>
<tr>
<td><input class="form-control" type="text" th:field="*{shippingAddress[1].street}"/></td>
<td><input class="form-control" type="text" th:field="*{shippingAddress[1].zip}"/></td>
</tr>
But I get information:
Invalid property 'shippingAddress[0]' of bean class [model.Client]: Property referenced in indexed property path 'shippingAddress[0]' is neither an array nor a List nor a Set nor a Map; returned value was [ShippingAddress(id=null, street=null, zip=null, state=null, city=null, country=null)]
To add values to Set I should use the add method? But how to implement it with Thymeleaf?
Method in Controller (save data):
#Transactional
#RequestMapping(value = "add", method = RequestMethod.POST)
public String saveClient(#ModelAttribute Client client) {
clientRepository.save(client);
return "redirect:/";
}
Method in Controller (open create form)
#RequestMapping("/create")
public String newClient(Model model) {
model.addAttribute("client", new Client());
return "create";
}

Related

Post Object with nested List object in Spring-boot and Thymeleaf

Spring/Thymeleaf beginner sorry in advance but I have 2 entities Employee and MeetingInfo. Employee has a oneToMany relationship with MeetingInfo so basically an Employee can have many MessageInfo. Using psvm I can add a new Employee with multiple MessageInfo to my database using something like this:
Employee employee1 = new Employee("Employee 1");
MeetingInfo mInfo1 = new MeetingInfo(LocalDate.of(2021, 1, 1), "First Random message");
MeetingInfo mInfo2 = new MeetingInfo(LocalDate.of(2021, 2, 2), "Second Random message");
MeetingInfo mInfo3 = new MeetingInfo(LocalDate.of(2021, 3, 3), "Third Random message");
employee1.getMeetingInfo().add(mInfo1);
employee1.getMeetingInfo().add(mInfo2);
employee1.getMeetingInfo().add(mInfo3);
employeeRepository.save(employee1);
But how can I do this with a form in thymeleaf? I can add just an employee, but cant add a new MeetingInfo object. When I do I get a passException error.
My new_employee.html
<form action="#" th:action="#{/ines/saveEmployee}" th:object="${employee}"
method="POST">
<input type="text" th:field="*{name}"
placeholder="Employee Name" class="form-control mb-4 col-4">
*** so if I remove between here***
<input type="date" th:field="*{meetingInfo.meetingDate}"
placeholder="Message Date" class="form-control mb-4 col-4">
<input type="text" th:field="*{meetingInfo.message}"
placeholder="Message" class="form-control mb-4 col-4">
*** and here***
*** how can I include a MessageInfo object with a new Employee?***
<button type="submit" class="btn btn-info col-2">Save Meeting</button>
</form>
My Controller
#GetMapping("/showNewEmployeeForm")
public String showNewEmployeeForm(Model model) {
Employee employee = new Employee();
model.addAttribute("employee", employee);
return "meeting/new_employee.html";
}
#PostMapping("/saveEmployee")
public String saveEmployee(#ModelAttribute("employee") Employee employee) {
employeeService.saveMessage(employee);
return "redirect:/ines/employees";
}
Employee
#Entity
#Table(name = "employee")
public class Employee {
#Id
#Column(name = "employee_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long employeeId;
#Column(nullable = false)
private String name;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "employee_id", referencedColumnName = "employee_id")
private List<MeetingInfo> meetingInfo = new ArrayList<>();
//Constructors, getters and setters
MeetingInfo
#Entity
#Table(name = "meeting_info")
public class MeetingInfo {
#Id
#Column(name = "meeting_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long meetingId;
private String message;
#Column(name = "meeting_date")
private LocalDate meetingDate;
//Constructors, getters and setters
Saving multiple entities with a single request isn't something that you would usually want to do with your Spring Boot app, however, since I understand that this is for practice only, you could do this by using a single DTO object that would hold the information for both entities:
public class EmployeeMeetingDTO {
private String employeeName;
private String meetingMessage;
private LocalDate meetingDate;
}
Your controller could then accept just a single DTO entity from the request:
#PostMapping("/saveEmployee")
public String saveEmployee(#ModelAttribute("employeeDto") EmployeeMeetingDTO employeeDto) {
employeeService.saveMessage(employeeDto);
return "redirect:/ines/employees";
}
And you can separately create both entities in your EmployeeService class. Your Thymeleaf form would then look something like this:
<form action="#" th:action="#{/ines/saveEmployee}" th:object="${employeeDto}"
method="POST">
<input type="text" th:field="*{employeeName}"
placeholder="Employee Name" class="form-control mb-4 col-4">
<input type="date" th:field="*{meetingDate}"
placeholder="Message Date" class="form-control mb-4 col-4">
<input type="text" th:field="*{meetingMessage}"
placeholder="Message" class="form-control mb-4 col-4">
<button type="submit" class="btn btn-info col-2">Save Meeting</button>
</form>

Why got Invalid property 'district' of bean class

While run spring boot project, got error in home page Invalid property 'district' of bean class.
I know why this error is coming because district is property of child entity and i can pass parent entity from Home()method in controller. I could pass Person entity in model in Home() method. but district and city property is from Address entity I am working with OneToOne relationship mapping.
My question are below:
Can we get two entity together in th:object in thymeleaf
Can we send Address and Person entity together using Model from controller to view
Stacktrace:
Caused by: org.springframework.beans.NotReadablePropertyException: Invalid property 'district' of bean class [com.rest.RestApiPojo.Entity.Person]: Bean property 'district' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
Here down is my code:
Entity
#Entity
#Table(name = "person_master")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long p_id;
private String name;
private String surname;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Address address;
// getter setter
}
#Entity
#Table(name = "address_master")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long a_id;
private String district;
private String city;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "address")
#JoinColumn(name = "p_id")
private Person person;
// getter setter
}
service
#Override
public Person addPersonAddress(Person person) {
return personRepo.save(person);
}
controller
#RequestMapping(value = "/", method = RequestMethod.GET)
public String Home(Model mdl)
{
mdl.addAttribute("persons", new Person());
return "register";
}
#RequestMapping(value = "/personaddress", method = RequestMethod.POST)
public String addPersonAddress(Model mdl, #ModelAttribute("person") Person person, HttpServletRequest req)
{
Address address = person.getAddress(); // get reference of person from parent table and store in child table
address.setDistrict(req.getParameter("district"));
address.setCity(req.getParameter("city"));
address.setPerson(person);
pojoService.addPersonAddress(person);
return "listofperson";
}
Thymeleaf
<form th:action="#{/personaddress}" th:object="${persons}" method="post">
<div class="container">
<h1 style="text-align: center">Add Person</h1>
<div class="row">
<div class="col-sm-12">
<div class="mb-3">
<label for="exampleFormControlInput1" class="form-label">Person name</label>
<input type="text" class="form-control" name="name" th:field="*{name}">
</div>
<div class="mb-3">
<label for="exampleFormControlInput1" class="form-label">Person surname</label>
<input type="text" class="form-control" name="surname" th:field="*{surname}">
</div>
<div class="mb-3">
<label for="exampleFormControlInput1" class="form-label">District</label>
<input type="text" class="form-control" name="district" th:field="*{district}">
</div>
<div class="mb-3">
<label for="exampleFormControlInput1" class="form-label">City</label>
<input type="text" class="form-control" name="city" th:field="*{city}">
</div>
<input class="btn btn-primary" type="submit" value="Submit">
</div>
</div>
</div>
</form>
Two entities are not required as they have already been mapped in the person class just write 'address.district' and 'address.city' in the thymeleaf, you will get it
This is regarding boilerplate code. you could add #Data to the class coming from Lombok library.
If you are not using lombok add setter and getter
public String getDistrict() {
return district;
}
public void setDistrict(String district) {
this.district= district;
}

How to post list of objects eg:- List<products> through a table which is inside a form in thymeleaf?

this is my html:
when I am seeing the preview in intelliJ of the same page, it is showing the right page but as soon as I run my program the and click on addInvoice button it is just showing the fields which are above the table only ....the table inputs are not showing in browser.The problem is inside<tr th:each product...>
<input type="hidden" th:field="*{primaryKey}"
placeholder="primaryKey" class="form-control mb-4 col-4">
<input type="text" th:field="*{invoiceNo}"
placeholder="invoiceNo" class="form-control mb-4 col-4">
....
....
<table border="1" class="table table-striped table-responsive-md">
<thead>
<tr>
<th>SNo</th>
<th>Product</th>
<th>Description</th>
<th>Price</th>
<th>Qty</th>
<th>Tax%</th>
<th>Total</th>
</tr>
</thead>
<tbody>
<tr th:each="product,listOfProductsStat:*{listOfProducts}">
<td th:text="${listOfProductsStat.count}">1</td>
<td>
<input type="text" th:field="*{listOfProducts[__${listOfProductsStat.index}__].productName}">
</td>
<td>
<textarea class="form-control rounded-0" name="description" id="description" rows="3"
maxlength="500"
placeholder="Description"
th:field="*{listOfProducts[__${listOfProductsStat.index}__].description}"></textarea>
</td>
<td>
<input type="number" name="price" id="price" class="form-control"
th:field="*{listOfProducts[__${listOfProductsStat.index}__].price}" placeholder="Price">
</td>
<td>
<input type="number" name="qty" class="form-control" id="qty" placeholder="Qty"
th:field="*{listOfProducts[__${listOfProductsStat.index}__].qty}">
</td>
<td>
<input type="number" name="taxPercent" class="form-control" id="taxPercent" placeholder="Tax(%)"
th:field="*{listOfProducts[__${listOfProductsStat.index}__].taxPercent}">
</td>
<td>
<input type="number" name="total" class="form-control" id="total" placeholder="Total"
th:field="*{listOfProducts[__${listOfProductsStat.index}__].total}">
</tr>
</tbody>
</table>
this is my entities:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class InvoiceInfo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long primaryKey;
// private int SNo;
private String invoiceNo;
private String billTo;
private String status;
#DateTimeFormat(pattern = "yyyy-MM-dd")
private Date invoiceDate;
#DateTimeFormat(pattern = "yyyy-MM-dd")
private Date dueDate;
private double grandTotal;
#OneToMany(mappedBy = "invoiceInfo",cascade = CascadeType.ALL)
private List<Product> listOfProducts=new ArrayList<>();
}
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long productId;
private String productName;
private String description;
private double price;
private int qty;
private int taxPercent;
private double total;
// private double subTotal;
// private int totalTax;
// private String invoiceNote;
#ManyToOne
#JoinColumn(name = "invoice_info_primary_key")
private InvoiceInfo invoiceInfo;
this is my controller:
#Controller
public class InvoiceInfoController {
#Autowired
private InvoiceInfoService invoiceInfoService;
#GetMapping("")
public String InvoiceInfoListPage(Model model) {
model.addAttribute("InvoiceInfoList", invoiceInfoService.getAllInvoiceInfo());
model.addAttribute("products",invoiceInfoService.getAllProducts());
return "Home";
}
#GetMapping("/openNewInvoiceInfoForm")
public String openNewInvoiceInfoForm(Model model) {
InvoiceInfo invoiceInfo = new InvoiceInfo();
model.addAttribute("invoiceInfo", invoiceInfo);
return "new_invoiceInfo";
}
#PostMapping("saveInvoice")
public String saveInvoiceInfo(#ModelAttribute("invoiceInfo") InvoiceInfo invoiceInfo) {
invoiceInfoService.saveInvoiceInfo(invoiceInfo);
return "redirect:/";
}
#GetMapping("/editInvoiceForm/{primaryKey}")
public String editInvoiceForm(#PathVariable(value = "primaryKey") long primaryKey , Model model) {
InvoiceInfo invoiceInfo= invoiceInfoService.getInvoiceByPrimaryKey(primaryKey);
model.addAttribute("invoiceInfo",invoiceInfo);
return "update_invoiceInfo";
}
#GetMapping("/deleteInvoiceByPrimaryKey/{primaryKey}")
public String deleteInvoiceByPrimaryKey(#PathVariable(value = "primaryKey") long primaryKey){
this.invoiceInfoService.deleteInvoiceByPrimaryKey(primaryKey);
return "redirect:/";
}
}
the problem:
<tr th:each="product,listOfProductsStat:*{listOfProducts}">
my code is not even showing the <td>enter image description here tags in browser ..
How should I associate the listOfProducts instance in InvoiceInfo class with the form inputs and save it in Product table one by one?
Please help!

Spring Thymelaf in form table list

I pass multiple object from my GET request page. And one of them is ReplacedPartList as a list of ReplacedPart.
ReplacedPart.java
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "replaced_part_id")
private Long replacedPartId;
#Column(name = "maintain_id")
private Long maintainId;
#Column(name = "part_serial_no")
private String partSerialNo;
#Column(name = "quantity")
private Long quantity;
#Column(name = "unit_price")
private Double unitPrice;
#Column(name = "total_price")
private Double totalPrice;
//GETTERS and SETTERS
Part of my controller's GET method
List<ReplacedPart> replacedPartList = new ArrayList<>();
for (int i = 0; i < 7; i++) {
ReplacedPart replacedPart = new ReplacedPart();
replacedPartList.add(replacedPart);
}
model.addAttribute("replacedPartList", replacedPartList);
part of my returned form
<tr th:each="replacedPart, stat : ${replacedPartList}">
<td th:text="${__${stat.index}__}"></td>
<td><input type="text" th:value="${replacedPartList[__${stat.index}__].partSerialNo}" th:field="${replacedPartList[__${stat.index}__].partSerialNo}"></td>
<td><input type="text" th:value="${replacedPartList[__${stat.index}__].quantity}" th:field="${replacedPartList[__${stat.index}__].quantity}"></td>
<td><input type="text" th:value="${replacedPartList[__${stat.index}__].unitPrice}" th:field="${replacedPartList[__${stat.index}__].unitPrice}"></td>
<td><input type="text" th:value="${replacedPartList[__${stat.index}__].totalPrice}" th:field="${replacedPartList[__${stat.index}__].totalPrice}"></td>
</tr>
error message
Neither BindingResult nor plain target object for bean name 'replacedPartList[0]' available as request attribute
And iy is just GET request not even POST. How can I solve this problem?
Your are not using the correct syntax as specified in the doc
Try this :
<tr th:each="replacedPart, rpStat : *{replacedPartList}">
<td th:text="${rpStat.index}"></td>
<td><input type="text" th:value="*{replacedPartList[__${rpStat.index}__].partSerialNo}" th:field="*{replacedPartList[__${rpStat.index}__].partSerialNo}"></td>
<td><input type="text" th:value="*{replacedPartList[__${rpStat.index}__].quantity}" th:field="*{replacedPartList[__${rpStat.index}__].quantity}"></td>
<td><input type="text" th:value="*{replacedPartList[__${rpStat.index}__].unitPrice}" th:field="*{replacedPartList[__${rpStat.index}__].unitPrice}"></td>
<td><input type="text" th:value="*{replacedPartList[__*{rpStat.index}__].totalPrice}" th:field="*{replacedPartList[__${rpStat.index}__].totalPrice}"></td>
</tr>
When using a list whether you want to show it so you have to not use it in a form. Forms ar binded to an object with the 'th:object' attribute. So if you will fill it it has to be a part of your model Maintain class.
Here is a full example on how to manipulate lists.
I solved my problem with MaintainFormDto class. I created as a form object and send this to view. And then I use * for the object binding like this.
MaintainFormDto
#Valid
private Maintain maintain;
#Valid
private Demand demand;
private List<ReplacedPart> replacedPartList;
public MaintainFormDto(Maintain maintain, Demand demand, List<ReplacedPart> replacedPartList) {
this.maintain = maintain;
this.demand = demand;
this.replacedPartList = replacedPartList;
}
//GETTER and SETTERS
MaintainController
MaintainFormDto formDto = new MaintainFormDto(maintain, demand, replacedPartList);
model.addAttribute("form", formDto);
form.html
<tr th:each="replacedPart, stat : *{replacedPartList}">
<td th:text="${__${stat.index}__}"></td>
<td><input type="text" th:value="*{replacedPartList[__${stat.index}__].partSerialNo}" th:field="*{replacedPartList[__${stat.index}__].partSerialNo}"></td>
<td><input type="text" th:value="*{replacedPartList[__${stat.index}__].quantity}" th:field="*{replacedPartList[__${stat.index}__].quantity}"></td>
<td><input type="text" th:value="*{replacedPartList[__${stat.index}__].unitPrice}" th:field="*{replacedPartList[__${stat.index}__].unitPrice}"></td>
<td><input type="text" th:value="*{replacedPartList[__${stat.index}__].totalPrice}" th:field="*{replacedPartList[__${stat.index}__].totalPrice}"></td>
</tr>

Spring web mvc Collection in entity class

Lets say i have 2 entities, Dish and Ingrediƫnt.
As we all know a Dish consists out of multiple Ingredients, so let's say:
Dish.java:
#Entity
public class Dish {
#Id
#GeneratedValue
protected long id;
private String name;
#OneToMany(mappedBy = "dish", cascade = CascadeType.PERSIST)
private Collection<Ingredient> ingredients;
//getters & setters
}
Ingrediƫnt.java :
#Entity
public class Ingredient {
#Id
#GeneratedValue
protected long id;
private String name;
//getters &setters
}
If we then have a DishController.java :
#Controller
public class DishController {
Service service;
public DishController() throws ServiceException {
service = new ShoppingFacade("JPA");
}
#RequestMapping("/showDishOverview")
protected ModelAndView getDishes() throws ServiceException {
Collection<Dish> dishes = service.getAllDishes();
return new ModelAndView("dish/dishOverview", "dishes", dishes);
}
#RequestMapping(value = "/showDishForm", method = RequestMethod.GET)
protected ModelAndView showDishForm(#RequestParam(value = "id") long dishId)throws ServiceException{
Dish dish = shoppingFacade.getDishById(dishId);
return new ModelAndView("dish/dishForm", "dish", dish);
}
#RequestMapping(value = "/editDish", method = RequestMethod.POST)
protected String updateDish(#ModelAttribute("dish") Dish newDish) throws ServiceException{
service.updateDish(newDish);
return "forward:/showDishOverview.htm";
}
}
Now lets say that we select one dish by its id:
${dish.getName()}
Now the showDishForm method in our controller will be called.
which will send us to dishForm.jsp:
<form method="post" action="editDish.htm">
<table>
<tr>
<td><label>Name </label></td>
<td><input type="text" name="name" placeholder="${dish.getName()}" value="${dish.getName()}"/></td>
</tr>
<tr>
<td><label>People</label></td>
<td><input type="text" name="people" placeholder="${dish.getPeople()}" value="${dish.getPeople()}"/></td>
</tr>
<tr>
<td colspan="2">
<input type="hidden" name="ingredients" value="${dish.getIngredient()}" />
<button type="submit" name="id" value="${dish.getId()}">Save</button>
</td>
</tr>
</table>
</form>
How will Spring MVC pass the info from my view to the controller?
Cause I get it working as long as I don't pass any Collections.(comment out <input type="hidden" name="ingredients" value="${dish.getIngredient()}" />
)
If i try to pass a Collection i get the message :
HTTP STATUS 400 - The request sent by the client was syntactically
incorrect
Spring uses Converters, for all standard classes these are already implemented.
For custon classes like this List, you have to write your own converter.

Categories