Spring MVC form radio input not updating model - java

I'm creating a web application using Spring MVC framework and forms.
I'm having trouble getting my form to update the field position in my PlayerModel. It simply doesn't save the value when I submit the form (check the controller inline comment on the submit() function).
If I select either radio button (with values 1 and 2) and submit, the model reaches the controller with value 0.
Despite having read countless similar questions/answers here on StackOverflow, I am unable to get this to work. What am I doing wrong here?
[EDIT]
I figured out the problem. For some reason, the value of the name attribute in the radio input is being used to match with the model attribute, instead of using path.
<input type="radio" id="index1" value="1" path="position" name="index" />
So it is trying to match index with the model, which of course does not exist, instead of using the position value in the path attribute.
Shouldn't it be the other way around?
playerView.jsp
<%# taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<html>
<head>
</head>
<body>
<form:form action="/game/playerView" method="POST" modelAttribute="playerModel">
<table>
<tr>
<th>
<input type="radio" id="index1" value="1" path="position" name="index" />
<input type="radio" id="index2" value="2" path="position" name="index"/>
</th>
</tr>
<tr>
<td><input type="submit" value="Submit"/></td>
</tr>
</table>
</form:form>
</body>
</html>
GameController.java
#Controller
#SessionAttributes("playerModel")
public class GameController {
#RequestMapping(value = "playerView", method = RequestMethod.GET)
public ModelAndView hello(ModelMap map) {
PlayerModel playerModel = new PlayerModel();
playerModel.setPosition(0);
map.addAttribute("playerModel", playerModel);
return new ModelAndView("playerView", "playerModel", playerModel);
}
#RequestMapping(value = "playerView", method = RequestMethod.POST)
public ModelAndView submit(#ModelAttribute("playerModel") PlayerModel playerModel, BindingResult result, ModelMap model){
playerModel.getPosition(); // returns 0
model.addAttribute("playerModel", playerModel);
return new ModelAndView("playerView", "playerModel", playerModel);
}
}
PlayerModel.java
#Resource
public class PlayerModel {
private int position;
public int getPosition() {
return position;
}
public void setPosition(int position) {
this.position = position;
}
}

You are using Spring-MVC form tag therefore please do not use this <input type="radio" id="index1" value="1" path="position" name="index" /> insted of use like this (For more details)
<tr>
<td>Sex:</td>
<td>
Male: <form:radiobutton path="sex" value="M"/> <br/>
Female: <form:radiobutton path="sex" value="F"/>
</td>
</tr>
and there is no path variable in HTML <input type="radio"> ,
path should be used in the spring type declaration.
eg :
<form:input path="firstName"/> this code is changed to <input name="firstName" type="text"/> by Spring

Related

How to have add and remove buttons in html form handle by spring 5 controller?

I am using Spring 5 and Hibernate 5.
I have got form like below in jsp file:
<form:form action="addUser" method="post" modelAttribute="user">
<table>
<tr>
<td>Name</td>
<td>
<form:input path="name" /> <br />
<form:errors path="name" cssClass="error" />
</td>
</tr>
<tr>
<td>Email</td>
<td>
<form:input path="email" /> <br />
<form:errors path="email" cssClass="error" />
</td>
</tr>
<tr>
<td colspan="1"><button type="submit">Submit</button></td>
<td colspan="1"><button type="???delete???">Remove</button></td>
</tr>
</table>
</form:form>
And UserController.java like below:
#Controller
public class UserController {
#Autowired
private UserService userService;
#GetMapping("/")
public String userForm(Locale locale, Model model) {
model.addAttribute("users", userService.list());
return "editUsers";
}
#ModelAttribute("user")
public User formBackingObject() {
return new User();
}
#PostMapping("/addUser")
public String saveUser(#ModelAttribute("user") #Valid User user, BindingResult result, Model model) {
if (result.hasErrors()) {
model.addAttribute("users", userService.list());
return "editUsers";
}
userService.save(user);
return "redirect:/";
}
}
Right now I have got a submit button which allow me to save name and email of the user.
I would like to add button REMOVE which would also relay on the same form but will instead of adding new user will be removing existing user.
Could you tell how can I do it?
Maybe the first option is to add some attribute like action, but then I need to handle it in controller and I don't know how?
Thank you.
It can be done by changing the form action dynamically based on the button click using some javascript.
<script>
$('#addBtn').click(function(){
$("#userForm").submit();
});
$('#removeBtn').click(function(){
$('#userForm').attr('action', '<your remove action>');
$("#userForm").submit();
});
<script>
<form:form action="addUser" method="post" modelAttribute="user" id="userForm">
...
<td colspan="1"><button type="button" id='addBtn'>Submit</button></td>
<td colspan="1"><button type="button" id='removeBtn'>Remove</button></td>
...
</form:form>

Passing parameters from JSP to Controller in URL issue

I want to update some user’s data and have issue with receiving parameters from JSP dropdown menu. I want to receive entered compId from “Enter PC” block and pass it as a PathVariable. But it is not seen. If I hardcode action="${app}/adminEdit.do/${user.userId}/${any number}" it works. So, question is – now to get this parameter from dropdown and set it to path? Thanks in advance.
Update.jsp snippet
<c:set var="app" value="${pageContext.request.contextPath}"/>
............
<DIV class="admin_redaction_block">
<sf:form name="adminUserUpdate"
method="POST"
modelAttribute="userForm"
action="${app}/adminEdit.do/${user.userId}/${comp.compId}"
enctype="application/x-www-form-urlencoded">
<c:if test="${not empty errorMsg}">
<div class="error">
<c:out value="${errorMsg}"/>
</div>
</c:if>
<sf:label path="password"><strong>Enter new password:</strong></sf:label> <br>
<sf:input path="password" type="text" size="20"/><br>
<sf:errors path="password" cssClass="error"/>
<br>
<sf:label path="email"><strong>Enter new Email:</strong></sf:label> <br>
<sf:input path="email" type="text" size="20"/><br>
<sf:errors path="email" cssClass="error"/>
<strong>PC Assigned:</strong>
<h3 class="h3">
<td>
<c:choose>
<c:when test="${user.computers!= null && !user.computers['empty']}">
<c:forEach items="${user.computers}" var="comp">
<c:out value="${comp.pcName}"/>
</c:forEach>
</c:when>
<c:otherwise>
<p class="h3_error">No PC Assigned</p>
</c:otherwise>
</c:choose>
</td>
</h3>
<sf:label path="computers">Enter PC:</sf:label> <br>
<sf:select path="computers" size="3">
<c:forEach items="${computers}" var="comp">
<sf:option value="${comp.compId}">
<c:out value="${comp.compId}"/>
</sf:option>
</c:forEach>
</sf:select>
<br> <br>
<input type="SUBMIT" name="SUBMIT" value="Update User"/>
</sf:form>
Controller
#RequestMapping(value = "/adminEdit.do/{userId}/{compId}", method = RequestMethod.POST)
public ModelAndView updateUserProcess(#ModelAttribute(value = "userForm")
UserForm userForm,
#PathVariable("userId") Integer userId,
#PathVariable("compId") Integer compId,
BindingResult result, Model model,
HttpSession session,
HttpServletRequest request) {
User user = userService.getUserById(userId);
model.addAttribute("computers", computerService.getAllComputers());
............
model.addAttribute("userForm", userForm);
return updatingUser(user, model, userForm);
}
You cannot.
You simply forgot that thing are written at different time.
<sf:form name="adminUserUpdate" ...
action="${app}/adminEdit.do/${user.userId}/${comp.compId}" ...>
is written at the time of answering the request that generates the form. At that time, your app (server side) is simply generating a HTML page, and the $comp.compid} does not exist. You can verify it by looking at the HTML source code of the page in your browser.
Later, when you click on the submitbutton, the browser gather data from input fields encode all and send it via a POST request to the action URL without changing it. Browser does not even know that you wrote ${app}/adminEdit.do/${user.userId}/${comp.compId} in your jsp : it only recieved a plain text string localhost:8080/adminEdit.do/2/
So ... try to get comp.compid from an input field of your form using a <sf:select> or <sf:checkboxes> tag.
Well, after long time of searching I've found now I can pass parameters from JSP to Controller. There are special class CustomCollectionEditor which helps pass even multiple select values.
Here is good example https://blog.codecentric.de/en/2009/07/multiple-selects-mit-spring-mvc-2/
And my snippet:
#InitBinder("userForm")
private void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Set.class, "computers", new CustomCollectionEditor(Set.class) {
#Override
protected Object convertElement(Object element) {
String pcName = null;
Set<Computer> computerSet = new LinkedHashSet<>();
if (element instanceof String && !((String) element).equals("")) {
pcName = (String) element;
}
return pcName != null ? computerService.getComputerByName(pcName) : null;
}
});
}

CRUD with Spring MVC - DRY Suggestions

I'm new to Spring MVC. I am writing a small app to learn. I am working on some basic CRUD. I am writing the same code more than once to edit/display a domain class. What would be a better/proper way to implement the edit and save method?
Thanks
The controller:
#RequestMapping(value="/userInfo/create/{id}")
public ModelAndView edit(#PathVariable Integer id, Model model)
{
logger.info("UserInfo edit {}", id);
UserInfo userInfo = userInfoService.get(id);
model.addAttribute("userInfo", userInfo);
model.addAttribute("parent" , userInfoService.get(userInfo.getParentId()));
model.addAttribute("allUsers", userInfoService.list());
model.addAttribute("affInfos", affInfoService.findAllByUserInfo(userInfo));
ModelAndView mv = new ModelAndView("userInfo/create", "command", model);
return mv;
}
#RequestMapping(value="/userInfo/save", method=RequestMethod.POST)
public ModelAndView save(#Valid #ModelAttribute("userInfo")UserInfo userInfo, BindingResult result, Model model)
{
logger.info("UserInfo save");
model.addAttribute("userInfo", userInfo);
model.addAttribute("parent" , userInfoService.get(userInfo.getParentId()));
model.addAttribute("allUsers", userInfoService.list());
model.addAttribute("affInfos", affInfoService.findAllByUserInfo(userInfo));
ModelAndView mv = new ModelAndView("userInfo/create", "command", model);
if(!result.hasErrors())
{
userInfoService.saveOrUpdate(userInfo);
model.addAttribute("flashMsg", "UserInfo saved!");
}
else
{
model.addAttribute("flashMsg", "Could not save.");
}
return mv;
}
The View:
<%#taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%#taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
<title>Create/Edit UserInfo</title>
</head>
<body>
<div style="margin-left:100px;font-weight: bold;font-size:16px;margin-bottom:20px;margin-top: 10px;">User Information</div>
<c:if test="${flashMsg != null}">
<div>${flashMsg }</div>
</c:if>
<form:form modelAttribute="userInfo" action="${pageContext.request.contextPath}/userInfo/save" method="post" >
<form:hidden path="id"/>
<div style="width:200px;margin-left:100px;margin-bottom:10px;">
<div style="margin-bottom:4px;font-weight: bold;">Name</div>
<div><form:input path="name"/></div>
</div>
<div style="width:200px;margin-left:100px;margin-bottom:10px;">
<div style="margin-bottom:4px;font-weight: bold;">Parent</div>
<div>
<form:select path="parentId" itemLabel="name" itemValue="id" >
<form:option value="-1">Choose Parent</form:option>
<form:options items="${allUsers}" itemLabel="name" itemValue="id"/>
</form:select>
</div>
</div>
<c:if test="${affInfos != null }">
<div>
<table style="width:600px;border:1px solid #ccc;" class="center ui-corner-all shadow zebra-striped">
<thead>
<tr>
<th>Macro</th>
<th>AffID</th>
<th><button type="button" class="btn shadow">New</button></th>
</tr>
</thead>
<tbody>
<c:forEach var="affInfo" varStatus="i" items="${affInfos }">
<tr>
<td><input type="text" name="macro_${affInfo.id}" id="macro_${affInfo.id}" value="${affInfo.macro}"></td>
<td><input type="text" name="affid_${affInfo.id}" id="affid_${affInfo.id}" value="${affInfo.affid}"></td>
<td><button class="btn shadow" type="button">Delete</button></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</c:if>
<div style="margin-left:100px;margin-top:10px;" >
<form:button class="btn shadow">Submit</form:button>
</div>
</form:form>
</body>
</html>
First you could of course create a method like this one :
private method init(){
model.addAttribute("userInfo", userInfo);
model.addAttribute("parent" , userInfoService.get(userInfo.getParentId()));
model.addAttribute("allUsers", userInfoService.list());
model.addAttribute("affInfos", affInfoService.findAllByUserInfo(userInfo));
}
But, if your controller is used for only one JSP page (JSPX is the recommend page type), you can use something like that for each attribute on your JSP Page :
#ModelAttribute("allUsers")
public List<User> populateUserInfoList() {
return userInfoService.list();
}
It will automatically add to the ModelAndView the attribute wich name is in the #ModelAttribute annotation. But be careful , this will be called each time you use your controller, wich could create useless called to the database if your controller do more than calling always the same kind of JSP which need the same data.
With this you don't need anymore this line :
model.addAttribute("allUsers", userInfoService.list());
Hope it helps you

FreeMarker Form for nested Object

I am trying to write freemarker template but could not able to parse with my object class.
My POJO is
public class Metrix {
#Id
String _id;
String loginId;
Date date;
List<MatrixDetail> headers;
//All getters and setters
}
public class MatrixDetail {
String header;
int time;
String detail;
//All getters and setters
}
//Controller after saving form
#RequestMapping(value = "/matrix/save", method = RequestMethod.POST)
public View saveMatrix(#ModelAttribute Metrix matrix, ModelMap model) {
System.out.println("Reachecd in matrix save" );
return new RedirectView("/TrackerApplication/header.html");
}
FTL template form part
<form name="matrix" action="matrix/save.html" method="post">
<table class="datatable" align:"center">
<tr>
<th>Login Id:</th> <th> <input type="text" name="loginId" value= ${matrixList.loginId} required /> </th>
</tr>
<tr> <td></td><td></td><td></td></tr>
<tr>
<th>Header</th> <th>Time</th> <th>Details</th>
</tr>
**// I am not getting how this nested object which is of type List<MatrixDetail>
// will get parse in my form.**
<#list matrixList.headers as header>
<spring:bind path = "MatrixDetail">
<tr>
<td> <input name = "header" value = ${header.header} /> </td>
<td> <input name = "time" value = ${header.time} /> </td>
<td> <input name = "detail" value = ${header.detail} /></td></tr>
</#list>
</table>
<input type="submit" value="Save" />
</form>
How can we write freemarker template for form processing of such kind of nested object?
I am getting issues in form submission.
I would strongly advise against this.
Forms might be displayable in email in some cases, but they may not always work in the email client, not to mention those that only ever read emails in text-only form won't be able to use them whatsoever.
If you need users to enter a form, link to a page on your site and have the form there instead.

Spring MVC getting value of element that is not an object property on the server

I have a jsp page which uses spring tag lib. I have elements on he page that is bind to properties of an object. I also have button values that are not bind to the POJO i am trying to get these values on the server. Under is the code
JSP
<body>
<form:form id="monitoringList" name="monitoringList" commandName="monitoring">
<h3>Monitoring For Criminals Victims/Wittiness</h3>
<h3>Crime Record - ${crimeRecNo}</h3>
<div id="victims">
<h3>Victims</h3>
<hr>
<input type="hidden" id="records" value="${records}"/>
<div id="citizen_row">
<label class="name"></label>
<form:input class="citizen" type="hidden" name="socialSecurityNumber" path="socialSecurityNumber"/>
<table border="1">
<tr>
<td><form:input type="hidden" path="crimeRecNo" name = "crimeRecNo"/>
<canvas id="photoCvs${citizen.socialSecurityNumber}" class="canvas" height="200" width="200"></canvas></td>
<td><label>Start Date : </label><form:input name= "monitoringStDate" path="monitoringStDate" id="monitoringStDate"/></td>
<td><label>End Date : </label><form:input name="monitoringEndDate" path="monitoringEndDate" id="monitoringEndDate"/></td>
<td>
<label>Monitoring Type : </label>
<form:select path="monitoringTypeId" name="monitoringTypeId" id="monitoringTypeId" title="Monitoring Type">
<form:options items="${monitoringType.monitoringTypeList}" itemValue="monitoringTypeId" itemLabel="monitoringTypeDesc" />
</form:select>
</td>
</tr>
</table>
<div><button id="action" onclick="submitPage('${pageContext.request.contextPath}/monitoringList.htm','POST');" type="button">Create Monitoring Records</button></div>
</div>
<!-- MySql first record starts at 0. So we need to send in the value 0 to get the first record. Create Record Navigation based on record count -->
<div id= "recordNavigation">
<c:forEach begin="0" end="${records - 1}" var="i">
<input type="submit" class="navigationbtns" id="page" onclick="submitPage('${pageContext.request.contextPath}/monitoringList.htm','POST');" value="${i}"/>
</c:forEach>
</div>
</div>
</form:form>
</body>
This is the controller and i am using request.getParameter to get the value of the button however the value id null when i click on the button which post me to the server
Controller
#RequestMapping(value = "monitoringList.htm", method = RequestMethod.POST)
public ModelAndView handleNextMonitoringPage(#ModelAttribute("crimeRecNo")Integer crimeRecNo, Model model,#ModelAttribute Monitoring monitoring, BindingResult result,ModelMap m,HttpServletRequest request,SessionStatus status, HttpSession session) throws Exception {
String p_page = request.getParameter("page");
logger.info("Page request was ::" + p_page);
//int page = 0;
myMonitoringTypeList.put("monitoringTypeList",this.monitoringTypeManager.getListOfMonitoringType());
model.addAttribute("monitoringType",myMonitoringTypeList);
Monitoring aMonitoringRecord = new Monitoring();
aMonitoringRecord = this.monitoringManager.getAMonitoringRecByCrimeRecNo(crimeRecNo, page);
int recordCount = this.monitoringManager.MonitoringRecords_RecordCount(crimeRecNo);
model.addAttribute("records",recordCount);
model.addAttribute("crimeRecNo", crimeRecNo);
model.addAttribute("monitoring", aMonitoringRecord);
return new ModelAndView(new RedirectView("monitoringList.htm"),"page",page);
}
you are missing the name attribute under which the value is being submitted
<button name="page" ....>
and
<input type="submit" name="page" ..../>

Categories