I am currently unsure on how should I be doing this. When the method = RequestMethod.POST, how should I bind the select picker to my object.
This is part of my form
<form th:action="#{/incidentDetail/update}" method="post" id="incidentDetailForm">
<div class="form-row">
<div class="form-group col-md-6">
<label for="location">Name</label> <input class="form-control"
type="text" name="ioName" id="ioName" th:value="${incident.ioName}" />
</div>
<div class="form-group col-md-3">
<label for="location" class="cols-sm-2 control-label">Preparation
By</label><span class="bg-danger pull-right"></span>
<div class="cols-sm-10">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-reorder fa"
aria-hidden="true"></i></span> <select class="form-control selectpicker"
th:object="${incident}" th:field="*{incidentPreparationBy}"
id="incidentPreparationBy" name="incidentPreparationBy"
roleId="incidentPreparationBy">
<option th:each="user: ${userList}" th:value="${incident.incidentPreparationBy}"
th:text="${user.name}"></option>
</select>
</div>
</div>
</div>
</form>
My controller
#RequestMapping(value = "/update", method = RequestMethod.POST)
public String registerIncidentPost(#ModelAttribute("incident") Incident incident, HttpServletRequest request)
throws Exception {
incidentService.save(incident);
return "redirect:/incidentDetail?id=" + incident.getId();
}
Part of Incident Entity
#ManyToOne
#JoinColumn(name = "user_id_preparation_by")
private User incidentPreparationBy;
You can acces the select by adding:
#RequestParam("incidentPreparationBy") String option as a method argument
to your controller method.
This way the string "option" will contain the selected value.
Like this:
#RequestMapping(value = "/update", method = RequestMethod.POST)
public String registerIncidentPost(#ModelAttribute("incident") Incident incident,
#RequestParam("incidentPreparationBy") String option, HttpServletRequest request)
throws Exception {
String incidentPrepartionBy = option; //incidentPrepartionBy will give you the selected value now.
incidentService.save(incident);
return "redirect:/incidentDetail?id=" + incident.getId();
}
Related
In my jsp there are two fields: 1 types of documents and 2 are statuses.
The challenge is that I can send the selection to my controller. I can not figure out jsp how to send by action these values at the click of a button
<div id="page-content-wrapper">
<div class="container-fluid">
<div class="card">
<div id="collapseSearchParams" class="collapse show" aria-labelledby="headingSearchParams" >
<div class="card-body">
<form:form action="${request.contextPath}/search" method="post">
<div class="row">
<div class="col-lg-3 col-md-4 col-sm-6">
<fieldset class="form-group">
<label class="form-label semibold" for="typeCode">${phDocType}</label>
<select class="form-control" id="typeCode">
<c:forEach var="type" items="${documentList}">
<option id = "${type.value}"> ${type.name} </ option>
</c:forEach>
</select>
</fieldset>
</div>
<div class="col-lg-3 col-md-4 col-sm-6">
<fieldset class="form-group">
<label class="form-label semibold" for="statusCode">${phOrderStatus}</label>
<select class="form-control" id="statusCode">
<option> A</ option>
<option> B</option>
<option> C</option>
<option> D</option>
</select>
</fieldset>
</div>
<div class="col-sm-12">
<button id ="btnSearchUsers" class="btn btn-sm btn-secondary" style="float: right; "><span class="fa fa-search"></span> <spring:message code="label.button.search" /></button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
How to get this data in the controller to process? What changes should I make in jsp and in the method?
#RequestMapping(value = "/perso")
public class PersoController {
#Autowired
private PersoService persoService;
private List<DocumentType> docTypeList = null;
#RequestMapping(value = "/list")
public String persoList(Principal principal, Model model) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
docTypeList = persoService.persoList(authentication, principal);
model.addAttribute("documentList", docTypeList);
return "admin/perso/list";
}
#RequestMapping(value = "/search", method = RequestMethod.POST)
public void searchOrders( ){
}
}
thanks in advance
It's better to use #RequestParam in your controller.
For Example:
#RequestMapping(value = "/bar")
public String testAction(#RequestParam String fieldName) {
//Do whatever you want to fieldName
return "view";
}
For your case
#RequestMapping(value = "/search", method = RequestMethod.POST)
public void searchOrders(#RequestParam String statusCode ,#RequestParam String typeCode)
{
//Do whatever you want
}
So I need to update the entities in my table with the selected values from dropdown lists from thymeleaf.
What I want to do is update the movie column based on the selections from dropdown list. But I have more than one dropdown list for each WeekMovie entity.
I cant figure out how to update them all.
My WeekMovies Class
#Entity
public class WeekMovies {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn(name="movie_id")
private Movie movie;
#ManyToOne
#JoinColumn(name="saloon_id")
private Saloon saloon;
//getter setters.
My Controller
#RequestMapping(value = "/update",method= RequestMethod.GET)
public String updateweekmovies(Model model){
List<WeekMovies> weekMoviesList = weekMovieService.findAll();
List<Movie> movieList= movieService.findAll();
model.addAttribute("weekMovieList",weekMoviesList);
model.addAttribute("movieList",movieList);
return "updateweekmovies";
}
#RequestMapping(value = "/update",method= RequestMethod.POST)
public String updateweekPost(Model model){
return "updateweekmovies";
}
My Thymeleaf
div class="container">
<div class="row">
<form class="form-horizontal" th:action="#{/week/update}" method="post"
enctype="multipart/form-data">
<fieldset>
<legend class="center-block">
Update Book Information<span style="font-size: small"> * is a
required field</span>
</legend>
<!-- category -->
<div class="form-group">
<label class="col-md-2 control-label" for="weekmovie">*
Category</label>
<div class="col-md-8">
<li th:each="weekmovie:${weekMovieList}">
<select th:value="${weekmovie.movie}" id="weekmovie" name="weekmovie" class="form-control">
<option value="" selected="selected" disabled="disabled">Please
select an option...</option>
<option th:each="movie:${movieList}" th:value="${movie.id}" th:text="${movie.title}"> </option>
</select>
</li>
</div>
</div>
<div class="form-group">
<div class="col-md-2"></div>
<div class="col-md-8">
<button type="submit" class="btn btn-success">Update Movie</button>
</div>
</div>
</fieldset>
</form>
</div>
I have a form with three select inputs, which are added from the db in the view. Everything is OK and the user can select for every select. The problem is that when I submit the form, the #ModelAttribute generates the same queries as the GET request (when these three selects are added into the view) and in the same, the select fields are empty after the POST.
#GetMapping("/road-assistance")
public String viewPage(Model model, Locale locale) {
model.addAttribute("roadAssistanceDto", roadAssistanceService.createRoadAssistanceDto(locale, null));
return "admin/roadassistance/view";
}
#PostMapping("/road-assistance")
public String createRoadAssistance(Model model, Locale locale,
final #Valid #ModelAttribute("roadAssistanceDto") RoadAssistanceDto roadAssistanceDto,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "redirect:/";
}
return "admin/roadassistance/view";
}
Why the #PostMapping generates the same queries and in the same time, I don't have the info in the view/model?
the view -
<div class="row card-pagging">
<div class="col-sm-2 mt-3 offset-sm-2">
<label data-th-text="#{control.panel.roadassistance.country}"
class="control-label"
for="country">Country</label>
<select id="country" data-th-field="*{country}" class="form-control">
<option data-th-each="countryList : ${roadAssistanceDto.countryList}"
data-th-value="${countryList.id}"
data-th-text="${countryList.countryI18n[#locale].name}"></option>
</select>
</div>
<div class="col-sm-3 mt-3">
<label data-th-text="#{control.panel.roadassistance.region}" class="control-label"
for="region">Region</label>
<select data-th-field="*{region}" id="region" class="form-control">
<option value='' data-th-text="#{control.panel.roadassistance.chooseregion}"></option>
<option data-th-each="reg : ${roadAssistanceDto.regionList}" data-th-value="${reg.id}"
data-th-text="${reg.regionI18n[#locale].name}"></option>
</select>
</div>
<div class="col-sm-3 mt-3">
<label data-th-text="#{control.panel.roadassistance.city}" class="control-label"
for="region">City</label>
<select data-th-field="*{city}" id="city" class="form-control">
<option value="" data-th-text="#{control.panel.roadassistance.chooseregion}"></option>
<option data-th-each="city : ${roadAssistanceDto.cityList}" data-th-value="${city.id}"
data-th-text="${city.cityI18n[#locale].name}"></option>
</select>
</div>
</div>
And the RoadAssistanceDto -
public class RoadAssistanceDto implements Serializable {
private Long id;
private Country country;
private Region region;
private City city;
private List<Country> countryList;
private List<Region> regionList;
private List<City> cityList;
/* GETTERS AND SETTERS /*
}
Hm... I think that I notice the problem.. there is something workng when I'm binding the fields in the thymeleaf... but anyway, why is generating these queries?
I'm using a combination of Annotation validation and a Custom Validator
Object:
#Entity
#Table(name = "monitoringsystems")
public class MonitoringSystem {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#NotNull(message = "Name must not be empty.")#Size(min=1, message="Name must not be empty.")
private String name;
#NotNull(message = "URL must not be empty.")#Size(min=1, message="Name must not be empty.")
private String url;
#NotNull(message = "Username must not be empty.")#Size(min=1, message="Name must not be empty.")
private String username;
#NotNull(message = "Password must not be empty.")#Size(min=1, message="Name must not be empty.")
private String password;
#NotNull(message = "Confirm Password must not be empty.")#Size(min=1, message="Name must not be empty.")
#Transient
private String passwordConfirm;
CustomValidator:
#Component
public class MonitoringSystemValidator implements Validator {
#Override
public boolean supports(Class<?> type) {
return MonitoringSystem.class.isAssignableFrom(type);
}
#Override
public void validate(Object o, Errors errors) {
MonitoringSystem monitoringSystem = (MonitoringSystem) o;
if(!monitoringSystem.getPassword().equals(monitoringSystem.getPasswordConfirm())){
errors.rejectValue("passwordConfirm", "Passwords are not equal.");
}
}
}
I initialize the custom validator in my controller and set the mapping for the form and the saving method.
Controller:
#Controller
public class MonitoringSystemController {
#Autowired
private MonitoringSystemValidator monitoringSystemValidator;
#InitBinder
public void dataBinding(WebDataBinder binder) {
binder.addValidators(monitoringSystemValidator);
}
#RequestMapping("/monitoringsystem/new")
public String newMonitoringSystem(Model model, HttpServletRequest request) {
MonitoringSystem monitoringSystem = new MonitoringSystem();
model.addAttribute("monitoringSystem", monitoringSystem);
request.getSession().setAttribute("anonymization", monitoringSystem.getAnonymization());
request.getSession().setAttribute("hosts", monitoringSystem.getHosts());
return "monitoringsystem/form";
}
#RequestMapping(value = "/monitoringsystem/save", method = RequestMethod.POST)
public String save(#Valid MonitoringSystem monitoringSystem, BindingResult result, HttpServletRequest request, Model model) {
if(result.hasErrors()){
model.addAttribute("monitoringSystem", monitoringSystem);
request.getSession().setAttribute("anonymization", request.getSession().getAttribute("anonymization"));
request.getSession().setAttribute("hosts", request.getSession().getAttribute("hosts"));
return "monitoringsystem/form";
}
//more code
In a first step I only want to change the CSS of my fields (I use bootstrap) so display the errors.
Form:
<form class="form-horizontal" th:modelAttribute="monitoringSystem" th:object="${monitoringSystem}" th:action="#{/monitoringsystem/save}" method="post">
<input type="hidden" th:field="*{id}"/>
<fieldset>
<legend>New Monitoring-System</legend>
<div class="form-group" th:classappend="${#fields.hasErrors('name')} ?: 'has-error has-danger'">
<label class="col-md-4 control-label" for="textinput">Systemname</label>
<div class="col-md-5">
<input th:field="*{name}" class="form-control input-md" type="text" />
</div>
</div>
<div class="form-group" th:classappend="${#fields.hasErrors('url')} ?: 'has-error has-danger'">
<label class="col-md-4 control-label" for="textinput">URL</label>
<div class="col-md-5">
<input th:field="*{url}" class="form-control input-md" type="text" />
</div>
</div>
<div class="form-group" th:classappend="${#fields.hasErrors('username')} ?: 'has-error has-danger'">
<label class="col-md-4 control-label" for="textinput">Username</label>
<div class="col-md-5">
<input th:field="*{username}" class="form-control input-md" type="text" />
</div>
</div>
<div class="form-group" th:classappend="${#fields.hasErrors('password')} ?: 'has-error has-danger'">
<label class="col-md-4 control-label" for="textinput">Password</label>
<div class="col-md-5">
<input th:field="*{password}" class="form-control input-md" type="password" />
</div>
</div>
<div class="form-group" th:classappend="${#fields.hasErrors('passwordConfirm')} ?: 'has-error has-danger'">
<label class="col-md-4 control-label" for="textinput">Confirm Password</label>
<div class="col-md-5">
<input th:field="*{passwordConfirm}" class="form-control input-md" type="password" />
</div>
</div>
<div class="form-group">
<label class="col-md-4 control-label" for="singlebutton"></label>
<div class="col-md-4">
<a th:href="#{/monitoringsystem}" class="btn btn-default btn-small">Cancel</a> <button id="singlebutton" name="singlebutton" class="btn btn-primary btn-small">Submit</button>
</div>
</div>
</fieldset>
</form>
My validation is working correctly. The form is only saved if my fields are not null, the size is greater 1 and the password match. If not, my controller redirects me to the form.
The problem is, that my css don't change. So there must be a problem with my view-code or the errorBinding is not passed correctly to the view. But I can't find my mistake.
There was an error in my if condition which add the errorclasses. I had to change ${#fields.hasErrors('url')} ?: 'has-error has-danger' to ${#fields.hasErrors('*{name}')} ? 'has-error has-danger'
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>
....