I'm building simple twitter clone in Spring MVC. I want to provide edit functionality to posted messages.
Message domain object looks like this (simplified)
public class Message {
long id;
String text;
Date date;
User user;
}
I created jps form
<form:form action="edit" method="post" modelAttribute="message">
<table>
<tr>
<td><label for="text">Message: </label></td>
<td><form:textarea path="text" id="text"/></td>
</tr>
<tr>
<td><input type="submit" name="commit" value="Save" /></td>
</tr>
</table>
</form:form>
and added those method in controller class
#RequestMapping(value = "/edit", method = RequestMethod.GET)
public String showEditMessage(#RequestParam long id, Model model) {
Message message = messageService.findMessage(id);
if (message == null) {
return "404";
}
model.addAttribute("message", message);
return "users/editMessage";
}
#RequestMapping(value = "/edit", method = RequestMethod.POST)
public String editMessage(#Valid #ModelAttribute Message message, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "/users/editMessage";
}
messageService.updateMessage(message);
return "/users/editMessage";
}
The problem is that the Message received in editMessage() contains only text field. I assume that this is expected behaviour. Can it be configured to replace fields that are only in jsp form?
I know this is only one field and I could just use #RequestParam String message, but sooner or later I will face similar problem with more than just one field.
I also have side question.
Are attributes added in showEditMessage() are passed to editMessage() method? I tried to add "id" attribute in first method, but I couldn't retrive it using "#RequestParam long id" in second.
#SessionAttributes("message")
On top of controller class solved it.
Related
I have a list page as shown below and when I click on Edit button, I want to send the patientId to the getObject() so that I can load the object from DB if the patientId exists. I tried in many ways, but its taking null as the patiendId in getObject().
Any suggestions would be appreciated.
Following is my List page ::
<c:forEach var="pat" items="${patients}" varStatus="status">
<tr>
<c:url var="editUrl" value="/patient/${pat.patientId}"/>
<td>${pat.firstName}</td>
<td>${pat.mobileNumber1}</td>
<td>${pat.emailId1}</td>
<td><a href='<c:out value="${editUrl}"/>'>Edit</a></td>
</tr>
</c:forEach>
Controller code::
#Controller
#RequestMapping(value="/patient")
public class PatientController {
#ModelAttribute
public Patient getObject(#RequestParam(required=false) String patientId){
System.out.println("Model Attribute method :: "+patientId);
//Here I want to load the object using patientId.
return(patientId != null ? patientDAO.findPatientById(patientId) : new Patient());
}
#RequestMapping(value="/{patientId}", method=RequestMethod.GET)
public String editPatient(#PathVariable("patientId")String patientId){
System.out.println("Editing Id : "+patientId);
//Able to get the Id here.
return "editPage";
}
You are using #PathVariable in one place and #RequestParam in another and are are sending a path variable (/patients/123 rather than a request param /patients?patientId=123) : use one or the other.
Updating this as below should work:
#ModelAttribute
public Patient getObject(#PathVariable(required=false) String patientId){
System.out.println("Model Attribute method :: "+patientId);
//Here I want to load the object using patientId.
return(patientId != null ? patientDAO.findPatientById(patientId) : new Patient());
}}
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-modelattrib-methods
I got the answer. Below is my working code. Hope it will help someone.Thanks to all.
<tr>
<c:url var="deleteUrl" value="/patient/delete/${pat.patientId}"/>
<td>${pat.firstName}</td>
<td>${pat.mobileNumber1}</td>
<td>${pat.emailId1}</td>
<td>Edit</td>
<td><a href='<c:out value="${deleteUrl}"/>'>Delete</a></td>
</tr>
</c:forEach>
Controller code::
#ModelAttribute
public Patient getObject(#RequestParam(value="patientId", required=false) String patientId){
System.out.println("Model Attribute method :: "+patientId);
return (patientId != null ? patientDAO.findPatientById(patientId) : new Patient());
}
#RequestMapping(value="/newPatient", method=RequestMethod.GET)
public String demo(ModelMap model){
System.out.println("Demo of Patient");
return "newPatient";
}
In short, I have a Spring 3.1 MVC project and my controller doesn't respond to the POST request when I hit the submit button.
There is no error, just no response. The controller method is not being called. I have a logger in the method that displays an INFO message and nothing is displayed (other INFO messages do display). MVC is working (at least partial) because I get a response from a "home" JSP page, but nothing for a POST.
I'm including things that seem important; tell me if there's something you'd like to see.
Controller class:
#Controller
#RequestMapping(value = "/index")
public class Test {
#Autowired
private IAdminService service;
#RequestMapping(value = "/list", method = RequestMethod.GET)
public String lister(Model model) {
model.addAttribute("matieres", service.liserMatiere());
return "Action";
}
#RequestMapping(value="/saveMat", method = RequestMethod.POST)
public ModelAndView saveMat(#ModelAttribute("matiere") Matiere m) {
ModelAndView mav = new ModelAndView();
mav.addObject("mat", m);
service.ajouterMatiere(m);
mav.setViewName("Action");
return mav;
}
and this the NouvelleMat.jsp:
<%#taglib uri="http://www.springframework.org/tags/form" prefix="f"%>
<f:form class="form-horizontal" method="POST"
action="saveMat" modelAttribute="matiere">
<table>
<tr>
<td>Name:</td>
<td><f:input path="name" maxlength="30" /></td>
</tr>
<tr>
<td>Subject:</td>
<td><f:input path="subject" maxlength="50" /></td>
</tr>
<tr>
<td valign="top">Message:</td>
<td><f:textarea path="note" cols="70" rows="20" /></td>
</tr>
<tr>
<td><f:button type="submit" value="Submit matiere" name="submit" /></td>
<td> </td>
</tr>
</table>
</f:form>
I got Etat HTTP 400 The request sent by the client was syntactically incorrect
this is the matiere entity:
#Entity
public class Matiere implements Serializable{
#SuppressWarnings("unused")
private static final long serialVersionUID = 1L;
#Id
private String name;
private String subject;
private String note;
getters and setters....
}
First of all, i think slashes at the beginning of the method RequestMapping are unnecessary, they will be relative to classe's "/index".
But to debug such problem, You should check out the request your browser sends to the controller.
If You are using Chrome, go with ctrl+shift+c to open the console, or F12 for firebug in Firefox, or console in IE. Go to network, and check out what is Your browser sending. You will probably be able to tell what is the mistake by looking at the request body and headers.
I am using Spring form to get inputs from client (if i use normal html input). If i use Spring form input i got error : java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'enumLanguage' available as request attribute
this is my JSP:
<form:form commandname="enumLanguage" action="${pageContext.request.contextPath}/enumLanguage/create.action" method="post" modelAttribute="enumLanguage" >
<fieldset class="langStep">
<legend>Language Details</legend>
<table class="langpadding">
<tr>
<td><label>Name:</label></td>
<td><form:input path="name" cssClass="textbox2"></form:input></td>
<td><label class="llangpadding">Short Name:</label></td>
<td><form:input path="shortName" cssClass="textbox2"></form:input></td>
</tr>
</table>
Save<span class="icon icon3"></span>
</form:form>
and this is my Controller:
#RequestMapping( value="/enumLanguage/create.action", method=RequestMethod.POST)
public ModelAndView create(#ModelAttribute EnumLanguage enumLanguage) throws Exception {
ModelAndView mvc = null;
try{
List<EnumLanguage> enumLanguages = new ArrayList<EnumLanguage>();
enumLanguages.add(enumLanguage);
List<EnumLanguage> enumLanguagesList = enumLanguageService.create(enumLanguages);
mvc = new ModelAndView("setup/EnumLanguageList");
} catch (Exception e) {
}
return mvc;
}
Make sure you have your #ModelAttribute set to the model when rendering the view
Make sure you made available in the view a model attribute with a key enumLanguage which is the value of the commandname of the form.
So the controller method that returns the view containing the form that you posted should look something like this.
#RequestMapping(value = "/language-details.do", method = RequestMethod.GET)
public ModelAndView initLanguageDetailsView() {
ModelMap model = new ModelMap();
EnumLanguage enumLang = new EnumLanguage();
//setters blah blah
//...
//make it available to the view
model.addAttribute("enumLanguage", enumLang);
return new ModelAndView("language-details", model);
}
I develop an MVC application with spring framework and some other frameworks (and I'm a beginner). I have a controller to manage jsp handling, for example when I want add new person to my 'person list' I call a instantiate a person object and I pass it to the jsp view corresponding to the add method. And I do that by this a method like this:
#RequestMapping(value = "/persons/add", method = RequestMethod.GET)
public String getAdd(Model model) {
logger.debug("Received request to show add page");
// Create new UserDomain and add to model
// This is the formBackingOBject
model.addAttribute("personAttribute", new UserDomain());
// This will resolve to /WEB-INF/jsp/addpage.jsp
return "addpage-tiles";
}
My problem is that now, I want to pass to add to the model two different Objects, for example, I want to pass the 'new UserDomain()' and also an other object which is from an other table in my database, for example a 'new UserSecurity()'.
I think I should use a 'modelMap' instead of the 'model.addAttribute...', but I can't do this, so if someone could help me.
I get my model from the jsp by a code like :
<form:form modelAttribute="personAttribute" method="POST" action="${saveUrl}">
<table>
<tr>
<td><form:label path="firstName">First Name:</form:label></td>
<td><form:input path="firstName"/></td>
</tr>
<tr>
<td><form:label path="lastName">Last Name</form:label></td>
<td><form:input path="lastName"/></td>
</tr>
<tr>
<td><form:label path="userName">User name</form:label></td>
<td><form:input path="userName"/></td>
</tr>
<tr>
<td><form:label path="email">E-mail</form:label></td>
<td><form:input path="email"/></td>
</tr>
</table>
<input type="submit" value="Save" />
thank you a lot for helping me.
Simply passing more than one object to the view is not a problem -- just use model.addAttribute multiple times, and then you can access both objects.
However, if you want to edit more than one model in <form:form> you'll need to create a class that contains both of the objects:
public class UserDomainSecurity {
private UserDomain userDomain;
private UserSecurity userSecurity;
// getters and setters for both
}
Then pass an instance of this into the view:
model.addAttribute("userDomainSecurity", new UserDomainSecurity());
And use it in the form:
<form:form commandName="userDomainSecurity" method="POST" action="${saveUrl}">
...
<form:input path="userDomain.firstName"/>
....
<form:input path="userSecurity.someSecurityProperty"/>
It's sometimes annoying to have to create all these additional classes, but it is somewhat logical. The wrapper class creates kind of namespaces in the form and thus separates the individual objects that you want to edit.
I wanted to add on this as it's really the present problem I have encountered minutes ago.
In this case, I assumed that you're UserSecurity and UserDomain are relative to each other.
Let say you have,
public class UserDomain {
public UserSecurity userSecurity
public String firstName;
public String lastName;
// getters and setters...
}
and you have your UserSecurity something like,
public class UserSecurity {
public String someSecurityProperty;
// getters and setters...
}
Since userSecurity property can be accessed publicly, then you can just do what you have did in your Controller,
#RequestMapping(value = "/persons/add", method = RequestMethod.GET)
public String getAdd(Model model) {
logger.debug("Received request to show add page");
// Create new UserDomain and add to model
// This is the formBackingOBject
model.addAttribute("userDomainSecurity", new UserDomain());
// This will resolve to /WEB-INF/jsp/addpage.jsp
return "addpage-tiles";
}
then just access it in your addpage.jsp like it's an object property like below,
<form:form commandName="userDomainSecurity" method="POST" action="${saveUrl}">
...
<form:input path="firstName />
<form:input path="lastname />
....
<form:input path="userSecurity.someSecurityProperty"/>
as you notice, I access the someSecurityProperty by the property declared in UserDomain class.
Apologies if this question has been asked before. I'm hoping that someone can step in and help me figure out why my form validation errors aren't showing up.
I'm using Spring 3.0.3 and Hibernate, and I'm using jsr-303 validation to validate my form inputs. I have a Spring controller that handles GETting a page that contains a form that is created with the help of Spring's form taglib. In this form a user is able to change their name and have it saved to the database. If any of the input is empty then the page with the form should be displayed again with error messages. The same controller handles the page's submission. It seems that the controller is functioning properly in most respects, but when there is an error in the user submitted form, no errors are showing up on the page.
Here is what form looks like:
<form:form commandName="changeNameCommand">
<form:errors path="*" cssClass="errorBox" />
<table width="100%" border="0" cellspacing="5" cellpadding="5" align="left">
<tr>
<td><b>First Name:</b></td>
<td><form:input path="firstName" value="${user.firstName}" /></td>
</tr>
<tr>
<td><b>Last Name:</b></td>
<td> <form:input path="lastName" value="${user.lastName}" /></td>
</tr>
</table>
</form:form>
Note that there is a user object in the view that is used to populate the form with the user's current first and last name. This is working properly.
The controller looks something like this:
#Controller
#RequestMapping(value = "/account/settings/change-name")
#SessionAttributes("changeNameCommand")
public class ChangeNameController {
#ModelAttribute("changeNameCommand")
public ChangeNameCommand getCommand() {
return new ChangeNameCommand();
}
#RequestMapping(method = RequestMethod.GET)
public ModelAndView getChangeNamePage(HttpServletRequest req) {
ModelAndView mav = new ModelAndView("Account.ChangeName");
mav.addObject("page_title", "Change Name");
return mav;
}
#RequestMapping(method = RequestMethod.POST)
public String doChangeName(
#ModelAttribute("changeNameCommand")
#Valid ChangeNameCommand command,
BindingResult result, SessionStatus status) {
if (result.hasErrors()) {
return "redirect:/account/settings/change-name";
}
// Code here to persist updated user first and last name to database...
status.setComplete();
return "redirect:/home";
}
}
I'm using Tiles 2.1.2 to compose pages and Urlrewrite 3.1.0 to help form friendly urls.
The ChangeNameCommand class looks something like this:
import org.hibernate.validator.constraints.NotEmpty;
public class ChangeNameCommand {
#NotEmpty(message = "You must provide a first name.")
private String firstName;
#NotEmpty(message = "You must provide a last name.")
private String lastName;
#NotEmpty(message = "Your password is required to make changes.")
private String currentPassword;
// Getters and setters here...
}
When debugging, I see that when there is not input for either the first or last name the BindingResult instance does contain errors. My concern is the redirect when there is an error. I've seen other questions here that just return the view name instead of using the redirect: prefix. I tried that but (I think) because of the way I'm using Urlrewrite and the way my servlet mapping is set up Spring returns an error. I've tried returning
/account/settings/change-name
/web/account/settings/change-name
/mywebsite/web/account/settings/change-name
but to no avail. FWIW, here is my servlet-mapping:
<servlet-mapping>
<servlet-name>mywebsite</servlet-name>
<url-pattern>/web/*</url-pattern>
</servlet-mapping>
Any help much appreciated!
using redirect makes all request attributes (including errors and the whole model) disappear. So, don't use redirect, or use the session to temporarily store the data, or use conversations. Or figure out how to use something like a flash-scope (I'm about to in a while)
Another thing - using UrlRewriteFilter with spring-mvc is uncalled for. You have full control over your beatuful REST-like URLs with spring-mvc only.
Here is how I solved my problem. To start off with, I didn't want to drop my use of UrlRewriteFilter and Tiles. However, the problem with this was that, in the case of errors, I couldn't just return the path, as indicated in the controllers RequestMapping annotation. Below is my solution, with the redirect removed in the case of errors, in doChangeName().
#Controller
#RequestMapping(value = "/account/settings/change-name")
#SessionAttributes("changeNameCommand")
public class ChangeNameController {
#ModelAttribute("changeNameCommand")
public ChangeNameCommand getCommand() {
return new ChangeNameCommand();
}
#RequestMapping(method = RequestMethod.GET)
public ModelAndView getChangeNamePage() {
ModelAndView mav = new ModelAndView("Account.ChangeName");
mav.addObject("page_title", "Change Name");
return mav;
}
#RequestMapping(method = RequestMethod.POST)
public ModelAndView doChangeName(#ModelAttribute("changeNameCommand") #Valid ChangeNameCommand command,
BindingResult result, SessionStatus status) {
if (result.hasErrors
ModelAndView mav = new ModelAndView("Account.ChangeName");
mav.addObject("page_title", "Change Name");
return mav;
}
// Code here to persist updated user first and last name to database...
status.setComplete();
RedirectView view = new RedirectView("/home");
return new ModelAndView(view);
}
}
Thanks to everyone who helped me out on this!