I've got a form with a dropdown:
<div class="form-group">
<form:label path="departments">Dept. Code</form:label>
<form:select path="departments" items="${departmentMap}" multiple="true" />
departmentMap comes from the controller method:
#RequestMapping(value = "/officeForm", method=RequestMethod.GET)
public ModelAndView showOfficeForm() {
ModelAndView result = new ModelAndView("officeForm", "command", new Office());
List<Department> departmentsToDisplay = departmentServiceImpl.findAll();
Map<Department, String> departmentMap = new HashMap<Department, String>();
for (Department d : departmentsToDisplay) {
departmentMap.put(d, d.getDepartmentName());
}
result.addObject("departmentMap", departmentMap);
return result;
}
POST method:
#RequestMapping(value = "/addOffice", method = RequestMethod.POST)
public ModelAndView updateOffice(#ModelAttribute("office") Office office, BindingResult result) {
System.out.println("Office Name: " + office.getOfficeName());
System.out.println("Departments: " + office.getDepartments());
return new ModelAndView("result", "command", office);
}
Excerpt from Office.java:
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "offices")
private List<Department> departments;
Excerpt from Department.java:
#ManyToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
#JoinTable(name="OFF_DEPT_T",
joinColumns={#JoinColumn(name="DEPT_ID", referencedColumnName="ID")},
inverseJoinColumns={#JoinColumn(name="OFF_ID", referencedColumnName="ID")}
)
private List<Office> offices = new ArrayList<Office>();
If I print the response.getAllErrors() I get:
Field error in object 'office' on field 'departments': rejected value [package.domain.Department#5597e5cf,package.domain.Department#2d14d0a7]; codes [typeMismatch.office.departments,typeMismatch.departments,typeMismatch.java.util.List,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [office.departments,departments]; arguments []; default message [departments]]; default message [Failed to convert property value of type 'java.lang.String[]' to required type 'java.util.List' for property 'departments'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [package.domain.Department] for property 'departments[0]': no matching editors or conversion strategy found]
Can anybody show me how to pass the object but display the String? Thanks.
Problem 1:
As you are missing initialisation of List departments. You should initialise it before putting it in model by replacing:
ModelAndView result = new ModelAndView("officeForm", "command", new Office());
with:
Office office = new Office():
office.setDepartments(new ArrayList<Department>()):
ModelAndView result = new ModelAndView("officeForm", "command", office);
Or if you don't want initialisation in controller, you can initialise it at the time of creation of Office object, like below:
private List<Department> departments = new ArrayList<Department>();
Problem 2:
As you want to bind custom object (Department) list in your select path, you need to provide a custom Property Editor to the data binder, like below:
First create a Property Editor class, something like this:
public class DepartmentEditor extends PropertyEditorSupport {
#Override
public void setAsText(String text) throws IllegalArgumentException {
Department department = new department();
department.setName(text);
setValue(department);
}
}
Then register the property Editor by registering it. You can register it, by simply putting an initBinder method in your controller class like below:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Department.class, new DepartmentEditor());
}
Now, your code should work fine.
I bound command to a new office attribute.
#RequestMapping(value = "/officeSearch", method=RequestMethod.GET)
public String showOfficesSearch(Model model) {
model.addAttribute("command", new Office());
return "officeSearch";
}
Related
I have two method in different controller class
#RequestMapping(value="/addHari")
public String menuAddHari(HttpServletRequest request,#RequestParam(value="addchkmerah",required = false)String status, Model model) throws ParseException {
Date hariKerja = new SimpleDateFormat("yyyy-MM-dd").parse(request.getParameter("addharikerja"));
java.sql.Date workDay = new java.sql.Date(hariKerja.getTime());
String keterangan = request.getParameter("addkolomket");
Time jamMasuk = hs.getTime(request.getParameter("addjammasuk"));
Time jamKeluar = hs.getTime(request.getParameter("addjampulang"));
month = request.getParameter("addBulan");
HariKerjaModel hm = new HariKerjaModel();
hm.setKeterangan(keterangan);
hm.setTanggal(workDay);
hm.setMerahBukan(status);
hm.setJamMasuk(jamMasuk);
hm.setJamKeluar(jamKeluar);
hs.saveDate(hm);
model.addAttribute("month", month);
return "redirect:/hariKerja";
}
and
#RequestMapping(value="/hariKerja")
public String menuHariKerja(Model model,HttpServletRequest request) {
List<HariKerjaModel> hk = new ArrayList<>();
String month = request.getParameter("bulansrc");
HariKerjaController h = new HariKerjaController();
String x=h.getMonth();
if(month==null) {
String month2 = (String) model.getAttribute("month");
}
hk = hs.readHariKerja(month);
model.addAttribute("ListHariKerjaModel", hk);
model.addAttribute("valueSelected",month);
return "hariKerja";
}
how to pass value variable "month" in method "menuAddHari" to variable "month2" in method "menuHariKerja" ?
You can achieve your requirement while redirect your request with path or query or body containing entity or fields which are url-form-encoded.
return StringBuffer("redirect:/your/other/controller/method/")
.append(param1).append("/").append(param2);
And you will be able to handle the request in your other controller with :
#GetMapping(value="/your/other/controller/method/{param1}/{param2}")
public String otherMethodInOtherController(#PathVariable("paramName1") String param1, #PathVariable("paramName2") String param2) {
// your 2sd controller code here...
return "your-view-name";
}
Of course, if you have many parameters to send, it will be better to set a ResponseEntity with BodyInserters inside the body of your request before to send it to your 2sd controller.
And don't forget not to use GET methods to forward sensitive user|system datas.
i have some question about Spring MVC annotation #ModelAttribute.
In first method named as "addProduct" i create Model model and after call model.addAttribute i can use "product" name in jsp file,for example product.getProductPrice.
But in second method named same as first,i added parameter
" #ModelAttribute("product") Product product ",but why??
If i will delete this annotation, my program works as same as before,please explain me)
Thank you very much,sorry for my English,i am from Ukraine)
#RequestMapping("/admin/productInventory/addProduct")
public String addProduct(Model model) {
Product product = new Product();
// add default for radio button!
product.setProductCategory("Mobile Phone");
product.setProductCondition("New");
product.setProductStatus("active");
model.addAttribute("product", product);
return "addProduct";
}
#RequestMapping(value = "/admin/productInventory/addProduct", method = RequestMethod.POST)
public String addProduct(#ModelAttribute("product") Product product, HttpServletRequest request) {
productDao.addProduct(product);
MultipartFile productImage = product.getProductImage();
String rootDirectory = request.getSession().getServletContext().getRealPath("/");
System.out.println(rootDirectory);
// product id as the file name
// !!!! TODO
// path = Paths.get(rootDirectory + "/WEB-INF/resources/image/" +
// product.getProductId() + ".png");
path = Paths.get("F:\\Spring\\eMusicStore\\src\\main\\webapp\\WEB-INF\\resources\\images\\"
+ product.getProductId() + ".png");
if (productImage != null && !productImage.isEmpty()) {
try {
productImage.transferTo(new File(path.toString()));
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Product image saving failed", e);
}
}
return "redirect:/admin/productInventory";
}
Purpose #ModelAttribute is bind param/properties from request a model object,
say #ModelAttribute("person") Person person in your method, it will bind properties from object such name, age to Person and construct a object out of it. It does not pass anything to your view, it job finishes once the request submitted. Not carried down to the view of that action.
In contrast, when you have Model model you are explicitly constructing an object with property added to its attribute. It will be carried down to your view unlike what #ModelAttribute does above
Currently, I want to return the return xml result with XML with the below format :
I tried to use something like this
#XmlRootElement(name = "item")
public class Book implements Serializable {
#XmlAttribute
public int getBookId() {
return bookId;
}
...
....
and
#XmlRootElement(name = "OneBoxResults")
public class JavaClazz {
private List<Book> OneBoxResults;
public List<Book> getOneBoxResults() {
return OneBoxResults;
}
#XmlElements(#XmlElement(name = "book", type = Book.class))
public void setOneBoxResults(List<Book> oneBoxResults) {
OneBoxResults = oneBoxResults;
}
...
However, the return result which I received is only Json format as below :
{"oneBoxResults":[{"bookId":1,"bookName":"Search
Deployment","update":"2014-01-07","description":"A successful deployment
typically involves the following
elements:","path":null},{"bookId":2,"bookName":"GSA
Information","update":"2015-01-07","description":"Configure the OneBox
module so it sends search queries to the provider (a custom
application","path":null}]}
I also attemped to create new format in controller as below :
#RequestMapping(value = "/rest.oneboxSample",produces = MediaType.APPLICATION_XML_VALUE, method = RequestMethod.GET)
public #ResponseBody String oneboxSample(){
String tmpOpenField = "<Field name=\"";
String tmpCloseField = "</Field>";
StringBuilder builder = new StringBuilder();
builder.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
builder.append("<OneBoxResults>").append("<resultCode>");
builder.append("Listbook").append("<resultCode>");
for(int i = 0; i < bookDao.getBooks().size(); i++){
Book tmpBook = bookDao.getBooks().get(i);
builder.append("<MODULE_RESULT>");
builder.append(tmpOpenField).append("bookId\">").append(tmpBook.getBookId()).append(tmpCloseField);
builder.append(tmpOpenField).append("bookName\">").append(tmpBook.getBookName()).append(tmpCloseField);
builder.append(tmpOpenField).append("update\">").append(tmpBook.getUpdate()).append(tmpCloseField);
builder.append(tmpOpenField).append("description\">").append(tmpBook.getDescription()).append(tmpCloseField);
builder.append(tmpOpenField).append("path\">").append(tmpBook.getPath()).append(tmpCloseField);
builder.append("</MODULE_RESULT>");
}
builder.append("</OneBoxResults>");
return builder.toString();
}
But the result is not good. It returned a string instead of xml format which we need.
Now, our system need to receive a xml format instead of an original xml format.
Please tell me know the way to do it .
The below is my source code which I wrote
https://www.dropbox.com/s/4tyg0kp7gkzodod/onebox-service.zip?dl=0
Thanks,
I have an MVC app that is creating new offices instead of updating them on when using an edit form. Please help me understand why this is happening.
Search method that populates the search results:
#RequestMapping(value = "/searchResults", method = RequestMethod.POST)
public ModelAndView search(#RequestParam String searchCriteria, HttpServletRequest request) {
List<Office> offices = officeServiceImpl.search(searchCriteria);
return new ModelAndView("searchResults", "offices", offices);
}
Here's what the link to the edit form looks like on the search results page:
Edit Office
Here is the Controller's edit GET method that populates the form with the existing Office:
#RequestMapping(value = "/{officeId}/edit", method = RequestMethod.GET)
#Transactional(noRollbackFor=NoResultException.class)
public ModelAndView initUpdateOfficeForm(
#PathVariable("officeId") Long officeId, Model model) {
Office office = officeServiceImpl.find(officeId);
//prepareEditFormModelAndView(office) just converts some objects to strings for typeahead form population
return prepareEditFormModelAndView(office);
}
Here is the edit POST method:
#RequestMapping(value = "/{officeId}/edit", method = RequestMethod.POST)
public ModelAndView processUpdateOfficeForm(#ModelAttribute("office") #Valid Office office,
BindingResult result, SessionStatus status) {
if (! "united states of america".equals(office.getFolderStrings().toLowerCase())) {
//This portion of code converts the typeahead strings to objects
result = tryCountries(office, result);
result = tryDepartments(office, result);
result = tryEmployees(office, result);
}
if (result.hasErrors()) {
return prepareEditFormModelAndView(office);
} else {
officeServiceImpl.save(office);
status.setComplete();
return new ModelAndView("editResult", "office", office);
}
}
officeServiceImpl calls officeRepositoryImpl method save which looks like:
#Override
public Office save(Office office) {
em.merge(office);
em.flush();
return office;
}
Thanks
Edit: Adding prepareEditFormModelAndView(office), This method attempts to build strings from associated objects:
#Transactional(noRollbackFor={NoResultException.class, IndexOutOfBoundsException.class})
private ModelAndView prepareEditFormModelAndView(Office office) {
String departmentStrings = "";
String employeeStrings = "";
List<OOM> officeOOMs = new ArrayList<OOM>();
StringBuilder sb = new StringBuilder();
try {
officeOOMs = oomServiceImpl.getOOMsForCurrentOffice(office.getId());
} catch (NoResultException e) {
officeOOMs = null;
}
for (OOM o : officeOOMs) {
try {
Employee tempEmployee = employeeServiceImpl.find(o
.getEmployeeId());
sb.append(tempEmployee.getDisplayName() + ", ");
} catch (NoResultException e) {
sb.append("Not found in system");
}
}
employeeStrings = sb.toString();
if ((! "".equals(office.getDepartmentStringsOnForm())) && office.getDepartmentStringsOnForm() != null) {
departmentStrings = office.getDepartmentStringsOnForm();
}
String folderStrings = "";
try {
folderStrings = kmlFolderServiceImpl.getInternationalOfficeString(office.getId());
LOGGER.info("Folder Strings: " + folderStrings);
} catch (NoResultException e) {
folderStrings = "";
LOGGER.info("Folder Strings: " + "no result");
}
boolean isInternational = office.isInternational();
ModelAndView result = new ModelAndView("editOfficeForm", "office", office);
result.addObject("departmentStrings", departmentStrings);
result.addObject("isInternational", isInternational);
result.addObject("folderStrings", folderStrings);
result.addObject("employeeStrings", employeeStrings);
return result;
}
I am adding a previous comment here, for better clarification. According to the OP the following fixes the problem:
When the ID is not in the form then when the model is posted back no ID is set to the entity making the persistence provider believe it is new entity.
Therefor the most obvious solution is to post the ID of the entity as well in the save operation (probably using a hidden field).
Another solution would be to try to load the entity in the database based on some business key
to see if the entity is new or not.
Hi I received next error during the redirect:
The request sent by the client was syntactically incorrect
URL which browser shows is: localhost:8080/Project/menu/main/home/0 and here my classes with redirects first - "from", second "to":
/*
* Get all possible values of menu and generate correct url to pages controllers
*
*/
#Controller
#SessionAttributes("menu")
public class MainMenuController {
#ModelAttribute
public Menu createMenu() {
return new Menu();
}
#RequestMapping(value = "/menu", method = RequestMethod.GET)
public String mainMenuResolver(#ModelAttribute Menu menu) {
menu.setMainMenu("first");
return "forward:/menu/first";
}
#RequestMapping(value = "/menu/{mainMenu}", method = RequestMethod.GET)
public String subMenuResolver(#PathVariable String mainMenu, #ModelAttribute Menu menu) {
menu.setMainMenu(mainMenu);
menu.setSubMenu("home");
return "forward:/menu/first/home";
}
#RequestMapping(value = "/menu/{mainMenu}/{subMenu}", method = RequestMethod.GET)
public String secMenuResolver(#PathVariable String mainMenu, #PathVariable String subMenu, #ModelAttribute Menu menu) {
menu.setMainMenu(mainMenu);
menu.setSubMenu(subMenu);
menu.setSecMenu("0");
if (menu.getMainMenu().equals("first")){
return "redirect:/menu/main/"+menu.getSubMenu()+"/"+menu.getSecMenu();
}
if (menu.getMainMenu().equals("second")){
return "redirect:/menu/religion/"+menu.getSubMenu()+"/"+menu.getSecMenu();
}
return "redirect:/menu/main/"+menu.getSubMenu()+"/"+menu.getSecMenu();
}
}
Second class:
#Controller
#SessionAttributes("menu")
public class FirstPageController {
#ModelAttribute
public Menu createMenu() {
return new Menu();
}
#RequestMapping(value = "/menu/main/{subMenu}/{secMenu}", method = RequestMethod.GET)
public ModelAndView menuResolver(#PathVariable String mainMenu, #PathVariable String subMenu,#PathVariable String secMenu, #ModelAttribute("menu") Menu menu) {
menu.setMainMenu(mainMenu);
menu.setSubMenu(subMenu);
menu.setSecMenu(secMenu);
if (menu.getSubMenu().equals("home")){
String title = "Project - Home Page";
return new ModelAndView("MainPage", "title", title);
}
String title = "Project - Home Page";
return new ModelAndView("MainPage", "title", title);
}
}
Solved: I solved it, there excess parameter in the method of the second class.
In cases like this it is very useful to have org.springframework.web loggin level set to DEBUG in log4j configuration
<logger name="org.springframework.web">
<level value="DEBUG" />
...
</logger>
E.g. when parameter is missing or cannot be converted to the required type there will be an exception details in the log.
In my case the reason of this error was that browser (Chrome, in my particular case) was sending the date from the <input type="date" ... /> to the server in the wrong format so server didn't know how to parse it.
As said ike3, using the detailed log aided a lot to find the solution for me. In my case it was a mismatch between #PathVariable without name specified, and the variable itself.
Something like this:
#RequestMapping("/user/{uname}")
public String doSomething(#PathVariable String username) { ...
Note the difference between "uname" and "username" !
There was an exception internally that wasn't raised and I couldn't see it until I set the log to INFO level.
In my case, it was also a problem of conversion, Spring was expecting an Integer however I was entering a String. Try to check what you have passed as parameters to the controller