Spring MVC form: parameter tag not connected to my Id objects?

I am trying to display a collection field of command objects inside a list box. Inside the collection there is a field, identifier and name. I want to use id as the value of the html parameter and the name as the option text. See code below;

<form:select id="customCollection" path="customCollection" size="10"> <form:options items="${command.customCollection}" itemValue="id" itemLabel="name"/> </form:select> 

The name prints fine, but the value remains blank. Here is the HTML output;

 <option selected="selected" value="">name-value</option> 


My initial assumption was that my data was incorrect, but after posting the following code on my page:

 <c:forEach items="${command.customCollection}" var="c"> ${c.id} : ${c.name} <br> </c:forEach> 

The identifier and name will be printed correctly. Therefore, my data is correctly delivered in my opinion. This makes me assume that I am either using the form: the parameters are incorrect, or some errors in the form fall: parameters.

Can someone help me here?

EDIT:
Thanks to the help of BacMan and delfuego, I was able to narrow this problem down to my binder.

I used to assign a value in my element to a row name, here is my original binder,

 binder.registerCustomEditor(Collection.class, "customCollection", new CustomCollectionEditor(Collection.class) { @Override protected Object convertElement(Object element) { String name = null; if (element instanceof String) { name = (String) element; } return name != null ? dao.findCustomByName(name) : null; } }); 

When I remove this code from my initBinder method, the string value is correctly inserted into the form, but I need customEditor to convert the specified value to a database object.

So this is my new attempt at linking;

 binder.registerCustomEditor(Collection.class, "customCollection", new CustomCollectionEditor(Collection.class) { @Override protected Object convertElement(Object element) { Integer id = null; if (element instanceof Integer) { id = (Integer) element; } return id != null ? dao.find(Custom.class, id) : null; } }); 

However, this causes the same behavior as the previous binder, and makes the value not displayable. Any ideas on what I'm doing wrong here?

EDIT 2:
As I mentioned above, if I comment on my custom binder, then the user object will load its identifier and name correctly for the part of the form submission, but then it will never be associated with the parent object when I try to save it. So I really think the problem is with my binder.

I placed debug statements inside my convertElement method. Everything looks as it should work, dao correctly pulls objects from the database. The only behavior that seems suspicious to me is that the convertElement method is called twice for each custom item.

+4
source share
3 answers

This is one of those problems that once I realized what was going wrong, I don’t understand how it worked in the first place.

I used CustomCollectionEditor completely wrong. According to Martin Deynum's message in this stream,

As I said in another thread, CustomCollectionEditor should create collections (List, Set ,?). Thus, he will fill the desired collection with elements of the desired type.

However, it is not intended to convert individual elements to value. It is designed to work with collections, not with a single instance of a role. You want 1 PropertyEditor to complete 2 tasks for you.

Thus, he created a unique collection for each element that ultimately received zero in the Spring code when he tried to generate the HTML.

Here is what I did

 binder.registerCustomEditor(Custom.class, new PropertyEditorSupport() { @Override public void setAsText(String text) { Custom custom = dao.find(Custom.class, Integer.parseInt(text)); setValue(Custom); } }); 

I don't know why my previous CustomCollectionEditor had ever worked with names as values.

+4
source

I will try to provide a working sample that can help debug the problem you are facing:

 <form:form commandName="client" method="post" action="${edit_client}"> <form:select path="country"> <form:options items="${requestScope.countries}" itemValue="id" itemLabel="name"/> </form:select> </form:form> 

Here is my Country class, which is a member variable in my command object.

 public class Country { private Integer id; private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(final Object anObject) { if (anObject == null) { return false; } else if (this == anObject) { return true; } else if (anObject instanceof Country) { final Country aCountry = (Country) anObject; Integer aCountryId = aCountry.getId(); if (aCountryId != null) { return aCountry.getId().equals(id); } } return false; } @Override public int hashCode() { return id; } } 

I use a special property editor in the initBinder method of my controller. I will not use the implementation because it uses a common implementation.

 binder.registerCustomEditor(Country.class, "country", editorServiceFactory.getPropertyEditor(Country.class, CustomPropertyEditor.class)); 

Here is the reference data (this method is called from the controller's referenceData method):

 public Map<String, List<?>> getDemographicReferenceData() { Map<String, List<?>> referenceData = new HashMap<String, List<?>>(); referenceData.put("countries", countryDAO.findAll()); return referenceData; } 

I am using Spring 2.5

+2
source

One thing that seems wrong to me is that command.customCollection used both to fill in the possible values ​​for entering the form, and to bind to the final values ​​selected by the user using this selection input. It does not make sense, at least least for me ... for example, if I had a selection form for selecting a state in the USA, I would fill this selection with a set of valid states, but I would bind the selection value in state one , which was ultimately selected by the user.

Try the following: create your customCollection object outside the context of your command object. In other words, right now your customCollection is a property of your command object; instead, pull this object out of the command object and make it your own page attribute. In the Spring MVC model, things that will be used as data sources for drop-down lists are not yet known as reference data; in SimpleFormController this data is populated in the corresponding name SipleFormController#referenceData . This separates two different concepts - the possible values ​​for the live selection in the reference data, and the final values ​​(values) selected by the user live in the command object attached to the form and / or input of the selection.

So, suppose this is in the SimpleFormController , try adding (or changing accordingly) the referenceData as such:

 @Override protected Map<?, ?> referenceData(HttpServletRequest request, Object command, Errors errors) throws Exception { CustomCollection customCollection = new CustomCollection(); // ...populate your custom collection request.setAttribute("customCollection", customCollection); return super.referenceData(request, command, errors); } 

And then change your value to:

 <form:select id="customCollection" path="command" size="10"> <form:options items="${customCollection}" itemValue="id" itemLabel="name"/> </form:select> 

It makes sense?

+1
source

All Articles