Today I am stuck with the spring-form with the POST method which doesn't give posted item to the Controller which I wanted. Here is my code.
Controller.java
#Controller
#RequestMapping("/cart")
public class CartController extends CommonController
{
#RequestMapping(value = "/add", method = RequestMethod.POST)
public ModelAndView addCart(#ModelAttribute("productList") Item item, BindingResult result,Model model){
System.out.println(item.getId()); /// <-- doesn't gives me the ID
return new ModelAndView("cart");
}
}
ProductList.jsp
/// Loop through the products of search itemlist and generates the forms with the correct items
<c:forEach var="item" items="${productList.items}" varStatus="status">
${item.name}
<div class="addCart">
<c:url value="/cart/add.html" var="addURL" />
<form:form method="POST" action="${addURL}" modelAttribute="productList">
<form:hidden path="items[${status.index}].id"/>
<input type="submit" class="addCartBtn" value="Add to cart" />
</form:form>
</div>
BackingBean.java
public class SearchForm implements Serializable
{
private Collection<Item> items;
private String term;
// getters and setters
}
The ${productList} is the backingbean which loops through all items.
I don't really know what the problem is why it isn't giving me the correct data it passed through the POST.
Many thanks.
Covert your spring:hidden tag to normal html hidden tag:
<form:hidden path="items[${status.index}].id"/>
to
<input type="hidden" name="id" value="${item.id}"/>
Related
I'm getting this exception while trying to update a row in my database. I performed a lot of research in Google all I find is that I should add #modelAttribute which is already done.
I also found that I need to add bindind result after #ModelAttribute but this also didn't work so I removed it. I'm using JPA for persistence to manipulate my data, spring boot and thymeleaf for my views.
These are my Controllers one for updating and rendering views
#GetMapping("/edit/{id}")
public ModelAndView UpdateList(#PathVariable(name="id") String id) {
ModelAndView mav = new ModelAndView("updateList");
com.pfe.ClientRest.model.Files files = fileServ.get(id);
mav.addObject("Files", files);
return mav ;
}
#PostMapping("/Save")
public String saveRepport(#ModelAttribute("Files") com.pfe.ClientRest.model.Files dbfile) {
fileServ.save(dbfile);
return "/redirect:/ListFile";
}
this is my entity class I have getter setters and constructors
#Table( name="Files")
#Entity
public class Files {
#GeneratedValue(generator = "uuid")
#GenericGenerator(name = "uuid", strategy = "uuid2")
#Id
private String id;
private String FileName;
private String Verif;
public String getId() {
return id;
}
this is my template.
<div class="container">
<h1> Modifier les informations du Rapports</h1>
<form action="#" th:action="#{/Save}" th:objects="${Files}"
method="post" >
<input type="text" th:field=*{id} readonly="readonly"/>
<input type="text" th:field="*{fileName}" placeholder="Nom du Fichier"
class="form-control mb-4
col-4">
<input type="text" th:field="*{verif}" placeholder="Accepted/Rejected"
class="form-control mb-4
col-4">
<button type="submit" class="btn btn-info col-2"> Mettre à jour</button>
</form>
</div>
The field names in the html page and entity class does not match. These are case sensitive. So the correct one should be
private String fileName;
private String verif;
I am trying to perform a simple submit operation from a form. I use spring boot framework with thyme leaf template for my project. Language used is java in eclipse IDE.
All I am looking to do is to take the empname and empid (refer Employee class) from the form and store it in a java object.
When I run the application, the application opens and when i navigate to edit.html, i get this error message in the browser -
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Mon Jun 18 16:14:40 EDT 2018
There was an unexpected error (type=Internal Server Error, status=500).
An error happened during template parsing (template: "class path resource [templates/edit.html]")
I also get this error message on the console -
Caused by: org.springframework.beans.NotReadablePropertyException: Invalid property 'empname' of bean class [com.cardinalcommerce.model.Employee]: Bean property 'empname' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
at org.springframework.beans.AbstractNestablePropertyAccessor.getPropertyValue(AbstractNestablePropertyAccessor.java:622) ~[spring-beans-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.beans.AbstractNestablePropertyAccessor.getPropertyValue(AbstractNestablePropertyAccessor.java:612) ~[spring-beans-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.validation.AbstractPropertyBindingResult.getActualFieldValue(AbstractPropertyBindingResult.java:104) ~[spring-context-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.validation.AbstractBindingResult.getFieldValue(AbstractBindingResult.java:228) ~[spring-context-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.web.servlet.support.BindStatus.(BindStatus.java:129) ~[spring-webmvc-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.springframework.web.servlet.support.RequestContext.getBindStatus(RequestContext.java:903) ~[spring-webmvc-5.0.6.RELEASE.jar:5.0.6.RELEASE]
at org.thymeleaf.spring5.context.webmvc.SpringWebMvcThymeleafRequestContext.getBindStatus(SpringWebMvcThymeleafRequestContext.java:227) ~[thymeleaf-spring5-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.spring5.util.FieldUtils.getBindStatusFromParsedExpression(FieldUtils.java:305) ~[thymeleaf-spring5-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.spring5.util.FieldUtils.getBindStatus(FieldUtils.java:252) ~[thymeleaf-spring5-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.spring5.util.FieldUtils.getBindStatus(FieldUtils.java:226) ~[thymeleaf-spring5-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.spring5.processor.AbstractSpringFieldTagProcessor.doProcess(AbstractSpringFieldTagProcessor.java:174) ~[thymeleaf-spring5-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:74) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
... 67 common frames omitted
This is my snippet of the html document where the error occurs.
<form class="form-horizontal" action="#" th:action="#{/employee/edit}" th:object="${employee}" method="POST">
<div class="form-group">
<label class="control-label col-sm-3">File Prefix:</label>
<div class="col-sm-7">
<input type="text" class="form-control" th:field="*{empname}" placeholder="Enter employee name" />
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-3">File Prefix:</label>
<div class="col-sm-7">
<input type="text" class="form-control" th:field="*{empid}" placeholder="Enter the employee ID" />
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-7">
<button type="submit" class="btn btn-default" id="blackButton" th:value="Submit">Submit</button>
<button type="reset" class="btn btn-default" id="blackButton" th:value="Reset">Cancel</button>
</div>
</div>
This is my class where with the setters and getters -
public class Employee {
private String empid;
private String empname;
public String getEmployeeId() {
return empid;
}
public void setEmployeeId(String empid) {
this.empid = empid ;
}
public String getEmployeeName() {
return empname;
}
public void setEmployeeName(String empname) {
this.empname = empname;
}
}
This is the controller snippet -
#Controller
#RequestMapping(value="/")
public class GreetingController {
private static final Logger logger = LoggerFactory.getLogger(GreetingController.class);
#Autowired
private SomeRecord someRecord;
#GetMapping("/")
public String greeting() {
return "about";
}
#RequestMapping("/about")
public String about() {
return "about";
}
#GetMapping("/edit")
public ModelAndView edit() {
ModelAndView modelAndView = new ModelAndView("edit");
modelAndView.addObject("employee", new Employee());
return modelAndView;
}
#PostMapping("/edit")
public ModelAndView createRecord(#Valid Employee employee, BindingResult result) {
ModelAndView modelAndView = new ModelAndView();
if (result.hasErrors()) {
logger.info("Validation errors while submitting form.");
modelAndView.setViewName("CreateRecord");
modelAndView.addObject("employee", employee);
return modelAndView;
}
someRecord.addRecord(employee);
modelAndView.addObject("allRecords", someRecord.getAllRecordData());
modelAndView.setViewName("recordsInfo");
logger.info("Form submitted successfully.");
return modelAndView;
}
#GetMapping("/view")
public String view() {
return "view";
}
}
Let me know if anything else is required.
Thanks for your help.
You should use *{employeeName} and *{employeeId} rather than *{empname} and *{empid}. (Matching the getters and setters, rather than your private variables.)
I'm currently trying to update some HTML based on two properties in my viewmodel which are hooked to two form groups. This is ASP.NET Core.
The two form groups:
<div class="form-group">
<label asp-for="ParticipantAmount">Antal deltagere</label>
<input asp-for="ParticipantAmount" onchange="Group()" class="form-control" type="number" max="1000" min="1" />
</div>
<div class="form-group">
<label asp-for="GroupAmount">Antal grupper</label>
<input asp-for="GroupAmount" class="form-control" onchange="Group()" type="number" max="20" min="1" />
<p id="GroupSize">0</p>
</div>
The properties in the viewmodel:
public int ParticipantAmount { get; set; }
public int GroupAmount { get; set; }
The javascript method in the script section:
function Group() {
var groups = Number('#Model.GroupAmount');
var participants = Number('#Model.ParticipantAmount');
var size = participants / groups;
document.getElementById('GroupSize').innerHTML = size;
}
When I try to log stuff to the console, it seems like the properties are not being updated in the viewmodel when the form inputs change.
I think I'm missing some basic knowledge about an important aspect of what I'm trying to do, but I can't seem to google the correct keywords. Hope you can lead me in the right direction.
Just to illustrate how AJAX calls work I made a minimalistic web application using jQuery which should be used for learning only, the code is not production-ready.
Razor view. I've added default values and IDs to your the input fields to identify them easier in JavaScript code.
<div class="form-group">
<label asp-for="ParticipantAmount">Antal deltagere</label>
<input asp-for="ParticipantAmount" onchange="Group()" class="form-control" type="number" max="1000" min="1" value="#Model.ParticipantAmount" id="ParticipantAmount" />
</div>
<div class="form-group">
<label asp-for="GroupAmount">Antal grupper</label>
<input asp-for="GroupAmount" class="form-control" onchange="Group()" type="number" max="20" min="1" value="#Model.GroupAmount" id="GroupAmount" />
<p id="GroupSize">0</p>`enter code here`
</div>
JavaScript code using jQuery to get/update values and make AJAX request.
<script>
function Group() {
$.ajax({
method: "POST",
url: "/Home/UpdateAmounts",
data: { ParticipantAmount: $('#ParticipantAmount').val(), GroupAmount: $('#GroupAmount').val() }
}).done(function (response) {
$('#GroupSize').html(response);
});
}
</script>
Controller and view model. Added a method for AJAX calls.
public class HomeController : Controller
{
public IActionResult Index()
{
return View(new ParticipantViewModel());
}
// You can call this method via AJAX to update your view model
[HttpPost]
public IActionResult UpdateAmounts(int participantAmount, int groupAmount)
{
// Validation for negative numbers, and groupAmount cannot be zero
if (participantAmount < 0 || groupAmount <= 0)
{
return Ok(0);
}
return Ok(participantAmount / groupAmount);
}
}
public class ParticipantViewModel
{
public int ParticipantAmount { get; set; }
public int GroupAmount { get; set; }
}
To make things nicer and more modern you could look into following example of a simple web application using database to store data, and using Knockout for UI.
I´m trying to create a form and validate its data via #Valid on the command object.
The validation performs well, but an error is ocurring going back to web.
This is what I have:
HTML
<div id="content" layout:fragment="contenido">
<div sec:authorize="isAnonymous()">
<form class="form-horizontal" action="#" th:action="#{register}" th:object="${userForm}" method="post">
<input type="hidden" name="_csrf" th:value="${_csrf.token}"/>
<fieldset>
<label for="alias" th:text="#{form.register.alias}">Alias</label>
<input id="alias" type="text" th:field="*{alias}" placeholder="Su alias" required="required" autofocus="autofocus"/>
<label for="pass" th:text="#{form.register.password}">Contraseña</label>
<input id="pass" type="password" th:field="*{password}" pattern="[\w\d-_]{5,15}" required="required" th:title="#{form.error.password}"/>
<p th:if="${#fields.hasErrors('password')}" th:errors="*{password}">Error en el dato ingresado</p>
<button type="submit" name="save" class="btn btn-primary" th:text="#{control.register}">Registrarme</button>
</fieldset>
</form>
</div>
</div>
Controller
#RequestMapping(value = "/register", params = {"save"}, method = RequestMethod.POST)
public String register (final ModelMap model, #Valid final UsuarioForm userForm, final BindingResult result) {
if (result.hasErrors()) {
return "register";
} else {
return "redirect:/" + HomeController.PAGE_NAME;
}
}
When Clicking on "submit" the "register" method is called, result.hasErrors() is true so the same page should be displayed, but this error occurs.
Stack
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'userForm' available as request attribute
org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:144)
org.thymeleaf.spring4.util.FieldUtils.getBindStatusFromParsedExpression(FieldUtils.java:396)
org.thymeleaf.spring4.util.FieldUtils.getBindStatus(FieldUtils.java:323)
org.thymeleaf.spring4.util.FieldUtils.getBindStatus(FieldUtils.java:289)
org.thymeleaf.spring4.processor.attr.AbstractSpringFieldAttrProcessor.processAttribute(AbstractSpringFieldAttrProcessor.java:98)
org.thymeleaf.processor.attr.AbstractAttrProcessor.doProcess(AbstractAttrProcessor.java:87)
org.thymeleaf.processor.AbstractProcessor.process(AbstractProcessor.java:212)
org.thymeleaf.dom.Node.applyNextProcessor(Node.java:1017)
org.thymeleaf.dom.Node.processNode(Node.java:972)
If I add "userForm" to the model in the Controller this way:
Controller Modified
#RequestMapping(value = "/register", params = {"save"}, method = RequestMethod.POST)
public String register (final ModelMap model, #Valid final UsuarioForm userForm, final BindingResult result) {
if (result.hasErrors()) {
model.addAttribute("userForm", userForm); //THIS LINE IS ADDED
return "register";
} else {
return "redirect:/" + HomeController.PAGE_NAME;
}
}
The error disappears, BUT... the expression in the HTML ${#fields.hasErrors('password')} results false, so I cant show the error messages to the user.
Any idea of why this behaviour is happening?
Thanks in advance!
PS: I am using Spring MVC 4.1.2 with Thymeleaf 2.1.4
This
public String register(final ModelMap model,
#Valid final UsuarioForm userForm,
final BindingResult result)
should be:
public String register(final ModelMap model,
#ModelAttribute("userForm") #Valid final UsuarioForm userForm,
final BindingResult result)
Notice the #ModelAttribute annotation.
I want to do a mcq with Spring mvc. I've got a class Mcq with a OneToMany relationship with the class Question, which has a OneToMany relationship with the class Answer. Thus Mcq have as property an Arraylist ListOfQuestions, and Question an Arraylist ListOfAnswers.
My controller is
#RequestMapping(value="displayMcq", method = RequestMethod.GET)
public String showMcq(Model model) {
Mcq mcq = mcqService.findById(new Long(1));
model.addAttribute("mcq", mcq);
return "displayMcq";
}
#RequestMapping(method = RequestMethod.POST)
public String displayQcmRepondu(#ModelAttribute("mcq2") Mcq mcq, BindingResult binding, SessionStatus status) {
if (binding.hasErrors()) {
return "displayMcq";
} else {
status.setComplete();
return "redirect:/mcqSuccess/";
}
}
and my view displayMcq.jsp is
<form:form modelAttribute="mcq" method="POST">
<ol>
<c:forEach items="${mcq.listOfQuestions}" var="question">
<li>
<c:out value="${question.label}" />
<br />
<ul>
<c:forEach var="answer" items="${question.listOfAnswers}">
<form:checkbox path="listOfQuestions" value="answer.id" label="${answer.label}" />
<br />
</c:forEach>
</ul>
</li>
</c:forEach>
</ol>
<input type="submit" value="Validate" />
</form:form>
My mcq is well displayed but the processing of the form fails. I stay on the displayMcq appearance with the error "Etat HTTP 405 - Request method 'POST' not supported".
So, could you explain me the problem, help me to proccess my mcq correctly and return the checked answers?
Note that your controller methods are mapped to different URLs (due to absence of value attribute on your POST method).
Since you don't have action attribute in <form:form>, it sends a POST request to the current page's URL on submit, but you don't have controller methods to handle POST request to that URL.
So, you need to map your POST method to the same URL as your GET method:
#RequestMapping(value="displayMcq", method = RequestMethod.POST)
public String displayQcmRepondu(...) { ... }
Thanks, that solved one problem, but when I validated, I had a message error like "Failed to convert property value of type java.lang.String[] to required type java.util.List for property...".
So I modified the controller method as
#RequestMapping(value="displayQcm", method = RequestMethod.POST)
public String displayQcmRepondu(#ModelAttribute ("mcqProcess") Mcq mcq, BindingResult binding, Model model, SessionStatus status) {
model.addAttribute("mcqProcess", mcq);
status.setComplete();
return "mcqSuccess";
}
where I want to display the checked answers in mcqSuccess.jsp but the mcq was not submitted. I show the page, but juste the written text.
If it can help, the mcqSuccess is
<h2>MCQ submitted</h2>
<ol>
<c:forEach items="${mcqProcess.listOfQuestions}" var="question">
<li>
<c:out value="${question.label}" />
<br/>
<ul>
<c:forEach var = "answer" items = "${question.listOfAnswers}">
<c:if test = "${answer.correct}">
<c:out value = "${answer.label}" />
</c:if>
<br/>
</c:forEach>
</ul>
</li>
</c:forEach>
</ol>
</div>