Bind list on button click Thymeleaf - spring

What I want is when I click id=addUnitCharge button, id = proName and id=proAge should add in to id=property_type_tbl body. And when I click submit button all values should assign to(id=code and id=school as values and id=property_type_tbl values to unitCharge list)
This is my PlAccount class
public class PlAccount{
private String code;
private String school;
private List<UnitCharge> unitCharge;
//Getters and Setters
}
UnitCharge class
public class UnitCharge{
private String proName;
private String proAge;
//Getters and Setters
}
My Controller class
#GetMapping(path = {"/product/add")
public String index(Model model) {
model.addAttribute("type", PlAccount.builder().build());
return "/loan/product/add";
}
#PostMapping("/coopmis/waterTariff/product/add")
public String save(#Valid PlAccount plAccount, BindingResult result){
plAccountService.savePlAccount(plAccount);
}
My html
<form method="post" action="/product/add" th:object="${type}">
<input type="number" id="code" class="form-control" th:name="code">
<input type="number" id="school" class="form-control" th:name="school">
//On button click below Items should add to `property_type_tbl` table.
<input type="number" id="proName" class="form-control" th:name="proName">
<input type="number" id="proAge" class="form-control" th:name="proAge">
<button type="button" class="btn btn-success" id="addUnitCharge" onclick="addRowPropertyType()"
th:text="#{add}">Add
</button>
<div class="form-group">
<button type="submit" class="btn btn-success " th:text="#{submit}">Submit</button>
</div>
</form>
<div class="form-group">
<table class="table table-striped id="property_type_tbl">
<thead>
<tr>
<th th:text="#{proName}"></th>
<th th:text="#{proAge}"></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
Highly appreciate your kind support.

Related

How to update a table row by passing data from a Bootstrap modal form

I'm fresh in web development and I'm facing some problems
I have an html page that displays a table. Each row contains data and an "edit" button.
What I want is the following:
I already could invoke the modal form and make it pop up with data corresponding to the row's data (through the button "edit") before I add this code to my form tag th:action="#{/countries/update/{id} (id =${countryToUpdate.id})}" and hence I get this following error:
org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'id' cannot be found on null
I know that the passed model attribute countryToUpdateis null
I'm asking if it's possible to pass a model attribute to a the bootstrap modal form
That's my html table:
<table class="table">
<tr>
<th>Id</th>
<th>Description</th>
<th>Capital</th>
<th>Code</th>
<th>Actions</th>
</tr>
<tr th:each="country:${countries}">
<td th:text="${country.id}"></td>
<td th:text="${country.description}"></td>
<td th:text="${country.capital}"></td>
<td th:text="${country.code}"></td>
<td>
<!--Edit button to invoke the bootstrap form-->
<div class="btn-group">
<a th:href="#{/countries/findById/{id} (id=${country.id})}" class="btn btn-primary editModalBtn" data-toggle="modal" data-target="#editModal">Edit</a>
</div>
</td>
</tr>
</table>
The bootstrap form:
<div class="modal fade" id="editModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">New message</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<!--the action attribute of the from indicates to where the form is gonna be submitted-->
<form th:action="#{/countries/update/{id} (id =${countryToUpdate.id})}" method="post" th:object="${countryToUpdate}">
<div class="form-group">
<label for="descriptionEdit" class="col-form-label">Description</label>
<!--name should correspond to the fields in the modal class-->
<input
type="text"
class="form-control"
id="descriptionEdit"
name="description"
>
</div>
<div class="form-group">
<label for="capitalEdit" class="col-form-label">Capital</label>
<input type="text"
class="form-control"
id="capitalEdit"
name="capital"
>
</div>
<div class="form-group">
<label for="codeEdit" class="col-form-label">Code</label>
<input type="text"
class="form-control"
id="codeEdit"
name="code"
>
</div>
<div class="form-group">
<label for="continentEdit" class="col-form-label">Continent</label>
<input type="text"
class="form-control"
id="continentEdit"
name="continent"
>
</div>
<div class="form-group">
<label for="nationalityEdit" class="col-form-label">Nationality</label>
<input type="text"
class="form-control"
id="nationalityEdit"
name="nationality"
>
</div>
<div class="modal-footer">
<!--submit type button should be within the form to execute the query-->
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Update</button>
</div>
</form>
</div>
</div>
</div>
</div>
My controller:
#GetMapping("countries")
public String findAll(Model model){
List<Country> listOfCountries = countryService.findAll();
model.addAttribute("countries", listOfCountries);
return "country";
}
#GetMapping("countries/findById/{id}")
#ResponseBody
public Country findById(#PathVariable int id, Model mod)
{
Country country = countryService.findById(id).get();
//I want to create a countryToUpdate attribute to use it in the modal form
mod.addAttribute("countryToUpdate", country);
return country;
}
#GetMapping("countries")
public String findAll(Model model){
List<Country> listOfCountries = countryService.findAll();
model.addAttribute("countries", listOfCountries);
return "country";
}
#PostMapping(value="countries/update/{id}")
public String update(#ModelAttribute("countryToUpdate") Country country, #PathVariable int id) {
//Get the country object from database through the passed id
Optional<Country> c = countryService.findById(id);
c.get().setDescription(country.getDescription());
c.get().setContinent(country.getContinent());
c.get().setCode(country.getCode());
c.get().setCapital(country.getCapital());
c.get().setNationality(country.getNationality());
System.out.println("this country description is " + country.getDescription());
countryService.save(c.get());
return "redirect:/countries";
}
Any help would be appreciated.

Spring MVC editable table (Thymeleaf)

I have 2 entities - Invoice and InvoiceProduct. Invoice class has a field List of InvoiceProduct because Invoice has #OneToMany mapping on InvoiceProduct.
I want to send the List of InvoiceProduct to Thymeleaf with with editable fields with a Submit button. When I click on Submit button, the new edited List should be returned to the controller. However, I am able to send the list to the view, but when I send the edited list to controller, it is being passed as 'null'.
update-invoice.html:
<form action="#" th:action="#{/api/invoice/saveInvoice}" th:object="${invoice}" method="POST">
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>PID</th>
<th>Quantity</th>
<th>Unit Price</th>
</tr>
</thead>
<tbody>
<tr th:each="temp : ${invoice?.invoiceProducts}" >
<td><input name="invoiceProducts[${tempStat.index}].productId" th:value="${temp.productId}"/></td>
<td><input name="invoiceProducts[${tempStat.index}].quantity" th:value="${temp.quantity}"/></td>
<td><input name="invoiceProducts[${tempStat.index}].price" th:value="${temp.price}"/></td>
</tr>
</tbody>
</table>
<button type="submit" class="btn btn-info col-2">Submit</button>
</form>
InvoiceController:
#PostMapping("/saveInvoice")
public String postInvoice(#ModelAttribute("invoice") Invoice invoice) {
logger.info("invoice products " + invoice.getInvoiceProducts());
return "dummy";
}
What else do I need to add so that my update-invoice.html passes the list to my controller ?
You can try something like below as an example for thymleaf:-
<form method="post" th:action="#{/users/}" th:object="${userInfo}" class="col card p-3 mb-5">
<div class="form-group">
<label for="firstName">First Name</label>
<input id="firstName" placeholder="Enter First Name" required type="text" th:field="*{firstName}"
class="form-control"/>
</div>
<div class="form-group">
<label for="lastName">Last Name</label>
<input id="lastName" placeholder="Enter Last Name" required type="text" th:field="*{lastName}"
class="form-control"/>
</div>
<div class="form-group">
<label for="role">Role</label>
<select id="role" required th:field="*{role}" class="form-control ">
<option value="" hidden>Select a role</option>
<option th:each="role : ${T(com.springhow.examples.springboot.thymeleaf.domain.entities.Role).values()}"
th:value="${role}"
th:text="${role}">
</option>
</select>
</div>
<input type="submit" class="btn btn-primary" value="Create User">
</form>
and into Controller:-
#RequestMapping(value = "/", method = RequestMethod.POST)
public String createUser(Model model, #ModelAttribute UserInfo userInfo) {
UserInfo user = userService.createUser(userInfo);
return "redirect:/users/";
}

form th:action not working using thymeleaf and spring

i am new in spring and thymeleaf, i am trying to submit a form, insert to database, but whe i am using form submission with submission it simply redirects to page and don't invoke to controller
i don't know why,
please, help
here is my Controller
#Controller
public class AdminController {
#Autowired
private CategoryServiceImpl categoryService;
#GetMapping("/adminPage")
public String index(){
return "adminPage";
}
#GetMapping("/categoryList")
public String showCategory(){
return "categoryList";
}
#GetMapping("form")
public String categoryForm(Model model, Category category){
model.addAttribute("category", category);
// model.addAttribute("add", true);
// categoryService.create(category);
return "admin/categoryForm";
}
#PostMapping("create")
public String addOrgCategory(#Valid Category orgCategory) {
categoryService.create(orgCategory);
return "redirect:/categoryList";
}
my html form is here
<form action="#" th:action="#{/create}" th:object="${category}" method="POST">
<div class="form-group">
<label for="name" class="text-dark font-bold">Category name</label>
<input id="name" type="text" class="form-control" th:value="${category} ? ${category.name} : ' '" th:field="*{name}">
</div>
<div class="form-group">
</div>
<button
type="submit" class="btn btn-success" data-toggle="tooltip"
data-placement="top" title="Tooltip on top">Create
</button>
</form>

How to perform arithmetic operations of two different data types in thymeleaf?

I have a piece of HTML where I need to multiply (or perform arithmetic operations) two values using thymeleaf.
Let's say my price is double and quantity is an integer.
Here's my sample code:
<fieldset class="form-inline" disabled >
<label for= "prodAmount">AMOUNT: </label>
<input type="number" class="form-control" th:text="${#aggregates.sum(product.![price * qty])}">
</fieldset>
Here's the error I'm getting:
Exception evaluating SpringEL expression: "#aggregates.sum(product.![price * qty])"
Also , I would also like to ask how to perform this when there's two or more values.
Here's my html form
<div class="container">
<div class="row">
<div class="col-lg-4 col-sm-12 col-xs-12">
<form th:action="#{/product/product-list}" method="post" th:object="${productEntity}">
<input type="hidden" th:field="*{pid}" th:value="${pid}" >
<fieldset class="form-group">
<label for="product_name">Product Name</label>
<input type="text" class="form-control" th:field="*{pName}" th:value="${pName}">
</fieldset>
<fieldset class="form-group">
<label for="price">Price</label>
<input type="number" class="form-control" th:field="*{price}" th:value="${price}" placeholder="0">
</fieldset>
<fieldset class="form-group">
<label for="pQty">Quantity</label>
<input type="number" class="form-control" th:field="*{qty}" th:value="${qty}" placeholder = "0">
</fieldset>
<fieldset class="form-group">
<label for="status">Status</label>
<select class="form-control" id="select_status" th:field="*{status}" th:value="${status}">
<option value="0">AVAILABLE</option>
<option value ="1">NOT AVAILABLE</option>
</select>
</fieldset>
<fieldset class="form-group">
<label for="image">Image</label>
<input id="input-b2" name="input-b2" type="file" class="file"
data-show-preview="false" th:field="*{image}" th:value="${image}">
</fieldset>
</form>
</div>
<div class="col-lg-8 col-sm-12 col-xs-12">
<table class="table table-inverse table-striped table-hover">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Price</th>
<th>Quantity</th>
<th>Status</th>
<th>Image</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr th:each="product : ${productTable} ">
<td th:text="${product.pid}"></td>
<td th:text="${product.pName}"></td>
<td th:text="${product.price}"></td>
<td th:text="${product.qty}"></td>
<td th:text="${product.status == '0'} ? 'Available' : 'Not Available'"></td>
<td th:text="${product.image}"></td>
<td>
<a th:href="#{/productlist/{id}(id = ${product.pid})}" class="btn btn-primary">Update</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="row float-right">
<div class="col-xd-12">
<div>
<fieldset class="form-inline" disabled >
<label for= "prodAmount">AMOUNT: </label>
<input type="number" class="form-control" th:text="${#aggregates.sum(product.{price * qty})}">
</fieldset>
<button type="button" class="btn btn-primary">Checkout</button>
</div>
</div>
</div>
</div>
Product Controller:
#RequestMapping(value="/product-list")
public ModelAndView shopView() {
ModelAndView mav = new ModelAndView();
mav.addObject("productTable", pRepo.findAll());
mav.addObject("productEntity",new ProductEntity());
mav.setViewName("/productlist");
return mav;
}
#RequestMapping(value = "/{id}",method = RequestMethod.GET)
public ModelAndView getProduct(#ModelAttribute ProductEntity productEntity, #PathVariable int id) {
ModelAndView mav = new ModelAndView();
mav.addObject("productTable",pRepo.findAll());
mav.addObject("productEntity", pRepo.findOne(id));
mav.setViewName("/productlist");
return mav;
}

How tell the view an error has occured from controller? (Spring MVC)

How can I say trigger error/validation messages in the view from the controller in a better way? Currently, I do this by sending boolean attributes. For example, in creating a product, I have two possible errors. Invalid format of UPC of a product, or duplicate upc. I also have a validati
#RequestMapping("/createProduct")
public String createProduct(Model model, #RequestParam(value = "name") String name,
#RequestParam(value = "upc") String upc, #RequestParam(value = "category") String categoryName,
#RequestParam(value = "description") String description, #RequestParam(value = "price") BigDecimal price,
#RequestParam(value = "stock") int stock){
model.addAttribute("activeTab", 3);
if(Validator.invalidUpcFormat(upc)){
model.addAttribute("invalidFormat", true); //trigger for invalid format
return "management";
}
Category category = productService.getCategory(categoryName);
Product product = new Product(upc, category, name, description, price);
InventoryProduct inventoryProduct = new InventoryProduct(product, stock);
try {
managerService.add(inventoryProduct);
model.addAttribute("productCreated", true);
} catch (DuplicateProductException e) {
model.addAttribute("upc", upc);
model.addAttribute("duplicateProduct", true); // trigger for duplicate product
}
return "management";
}
And here is my view:
<div id="menu3"
class="tab-pane fade <c:if test="${activeTab == 3}">in active</c:if>">
<div class="container-fluid" style="padding: 2%;">
<div class="row">
<div class="col-md-12"
style="padding-left: 15%; padding-right: 15%;">
<c:if test="${productCreated}">
<div class="alert alert-success fade in">
<a href="#" class="close" data-dismiss="alert"
aria-label="close">×</a> <strong>Success!</strong>
Product has been created!
</div>
</c:if>
<c:if test="${duplicateProduct}">
<div class="alert alert-warning fade in">
<a href="#" class="close" data-dismiss="alert"
aria-label="close">×</a> <strong>Oh no!</strong>
Product with the UPC ${upc} already exists!
</div>
</c:if>
<c:if test="${invalidFormat}">
<div class="alert alert-warning fade in">
<a href="#" class="close" data-dismiss="alert"
aria-label="close">×</a> <strong>Oops!</strong>
Invalid UPC format!
</div>
</c:if>
<form
action="${pageContext.request.contextPath}/manager/createProduct"
method="post">
<div class="form-group">
<label for="Name">Name </label> <input type="text" name="name"
class="form-control" required />
</div>
<div class="form-group">
<label for="UPC">UPC </label> <input type="number" name="upc"
class="form-control" required />
</div>
<div class="form-group">
<div class="form-group">
<label for="category">Category</label> <select
class="form-control" name="category" required>
<option selected disabled value="">SELECT CATEGORY</option>
<c:forEach items="${categories}" var="item">
<option>${item.getName()}</option>
</c:forEach>
</select>
</div>
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea class="form-control" rows="5" name="description"></textarea>
</div>
<div class="form-group">
<label for="price">Price </label> <input type="number"
name="price" class="form-control" required />
</div>
<div class="form-group">
<label for="stock">Stock </label> <input type="number"
name="stock" class="form-control" required />
</div>
<button type="submit" class="btn btn-primary">Add
product</button>
</form>
</div>
</div>
</div>
</div>
Is there a better of doing this other than sending boolean triggers?
You could use Spring BindingResult. This is typical filled with the result of Binding and Validation results. But you can also add errors by hand.
But first you need to refactor your code, so that you use an single command/form-backing object instead of all the #Param values
public class CreateProductCommand {
private String name;
private String upc;
private String categoryName;
.... //other fields
public CreateProductCommand (){} //parameter less conturctor
Getter+Setter
}
Controller
#RequestMapping("/createProduct")
public ModelAndView createProduct(CreateProductCommand createProductCommand, BindingResult bindingResult) //Binding result must be the parameter direct next to the object that should been validated!!!
{
if (someustomValidationForUcpFail()) {
bindingResult.rejectValue("upc", //the field name of the invalid field
"error.Message.Key",
"Default Error Message");
}
if (bindingResult.hasErrors()) {
ModelMap model = new ModelMap();
model.add("createProductCommand", createProductCommand);
return new ModelAndView("createForm", model)
} else {
Product product = ...
return new ModelAndView("showProduct", "product", product)
}
}
jsp:
You need to use springs form and input tag:
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:springForm="http://www.springframework.org/tags/form"
version="2.0">
....
<springForm:form action="<c:url value="/manager/createProduct">" method="POST" modelAttribute="createProductCommand">
<springForm:input path="name"/> <form:errors path="name" />
<springForm:input path="ucp"/> <form:errors path="ucp" />
....
</springForm:form>
....

Resources