How to bind a Set<CustomObject> in spring mvc form - java

I have a command object associated with a spring form controller:
public class PluginInstance {
private Set<PluginParameter> pluginParameters = new HashSet<PluginParameter>();
... some other string/long properties and getter setters...
}
the PluginParameter also have a Set in it which contain the values
public class PluginParameter {
private String parmName;
private Set<PluginParmvalue> pluginParmvalues = new HashSet<PluginParmvalue>();
...some other string/long properties and getter setters...
}
(Normally the pluginParmvalues will contain only one value, a list have been used for future expandability)
In the spring form I binding the values as
<form:input path="pluginParameters[${itemsRow.index}].pluginParmvalues[0].parmValue" />
but the thing is that there can be a form:select(to present multiple predefined options to the user) or form:input (user can input any value). This has to be decided from another object
public class PluginConfigParm {
private String parmName;
private ArrayList<String> choices;
...getter setters and other properties
}
where I have to compare the name of PluginConfigParm.paramName with PluginParameter.paramName when they match and PluginConfigParm.choices.size() > 0 then form:select will be shown populated with the values from PluginConfigParm.choices otherwise form:input will be shown.
The question is simple: How can I do that.
Any help will be highly appreciated.

By using List<> instead of Set<> in controller. Problem solved. May be Set<> has no getter/setter that can be bind with spring form.
So <form:input path="pluginParameters[${itemsRow.index}].pluginParmvalues[0].parmValue" />
and List<> in controller makes my life easier.

The Set is not an indexed collection so I could not work by using this syntax
pluginParameters[${itemsRow.index}].pluginParmvalues[0].parmValue

eg:
class person{
set name<string> = new HashSet<String>()
}
<input type="hidden" path="person.name" name="person.name" value="<%=valueStr%>"/>
take valueStr = "hello, world"
by giving it as comma seperated values.. set works
Its working for me

Related

How to take LIST input in #RequestParam in Spring MVC?

Heyy,
I want to take a list of data in my request param,here "personIdCollection" is a set of list but when i am hitting through postman i am getting a bad request.
Here is my code.
controller
#PostMapping("/face-tag-data")
public String getFaceTaggedData(#RequestParam String projectId,#RequestParam List<String> personIdCollection) {
return null;
}
and here is my ajax
var data = {};
data.personIdCollection = personIdCollection;
data.projectId = $("#projectId").val();
$.ajax({
type:'POST',
url:contextPath+'/face-tag-data',
data:data,
success:function(resp){
console.log(resp);
},
failure:function(resp){
console.log(resp);
}
});
This is working for me. I do not use an ajax-request but instead submit my form directly but it should work either way.
My controller looks like:
#RequestMapping(value="addSingleArticles", method=RequestMethod.POST)
public #ResponseBody String addSingleArticles(
ArticleSubmitData pupilPackageData,
HttpServletRequest request) {
... // do something with the data
}
As you can see I have defined my own composite type which consists of three lists. So you obviously can use it with only one list directly.
public class ArticleSubmitData {
private List<Article> singleArticles;
private List<Article> packageArticle;
private List<Article> popupArticles;
... // getter & setter, inner classes etc.
}
In my server page oder faclet I use the following html-code to make this work
...
<input id="" class="" type="text" name="singleArticles[${line.index}].engraving" />
...
So the trick is to define the variables on your webpage as an array and use this in your controller as a list. As you can see in my example I also use an inner class in my composite class which has extra attributes.

jsr-303 exclued some elements from list during validation

I have class like this:
class From{
private List<SubForm> subForms;
}
class SubForm{
#NotBlank
String name;
#Max(value=100)
Integer count;
...
public boolen isValidatable(){
....
}
}
I need to validate list of subForms inside Form, but I want to exclude element that return isValidatable=false.
I cannot chage Form object before Validator.validate() method. I can use standard annotations or write my own ConstraintValidator.
how can I do it better ?
I find solution.
I created my own annotation and add it above List I want validate
#ConditionallyValidatedListItems
private List<ConditionallyValidated> subforms = new ArrayList<>();
then when I create ConstraintValidator. I get validator inside isValide method and if it necessary validate item
Validation.buildDefaultValidatorFactory().getValidator();
//get item check if it necessry to validte it
Set<ConstraintViolation<ConditionallyValidated>> validate = validator.validate(conditionallyValidated);
I create example on the github
https://github.com/YaroslavTir/jsr303-ConditionallyValidatedListItems

Mapping object which containing a list in request - Spring MVC

Is it possible to wrap an object which contain some String and a List of another object as property from requst in spring mvc?
My classes are :
public class MyObj {
private String ma;
private String mb;
private List<SecObj> mc;
}
and:
public class SecObj {
private String sa;
private String sb;
}
I want to get an object of MyObj from the request.
In my JSP
<form:form action="" method="POST" commandName="myObj" >
<form:input path="ma" />
<form:input path="mb" />
.........
3 or 4 mc
..........
</form:form>
If it is possible then
what should i write int <form:input path="?" /> path?
how i receive it in the request parameter #RequestParam ?
If it not possible at once then
please tell me an good way to wrap this type of one to many relationship from request parameter
Regarding the list, take a look at this issue: Spring MVC : List<E> need to pass as command object . The example shows that you can then fill in each field of SecObj individually, assuming SecObj has a public default constructor.
You could also register a formatter for SecObj to allow Spring to silently convert a whole user input String to SecObj. See http://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#format-configuring-formatting-mvc for details.

JSF Java Beans - initialize and proceed

I am new to JavaBeans and I need a little help to keep my first little JSF-project going.
I am writing a little web application where a user can search with certain criteria for buildings. So the user enters in the search form 'location', 'property type', 'asking price', 'number of rooms' and 'living space'.
My managed bean accept the requiry with setter/getter and now the data is to be transmitted to a SQL class, where they are processed and matching search results are returned. It sounds simple, but I can not find a solution.
My managed bean looks like this now:
package beans
//import statements
...
#ManagedBean
#RequestScoped
public class PropertySearchBean {
private String _place
private String _propertyType
private double _askingPrice
private int _rooms
private double _livingSpace
public ArrayList<SearchResults> results = new ArrayList<SearchResults>();
// empty constructor
...
// getter and setter for these 5 user inputs
...
public void initializeSearchResults() {
// do the SQL query, recieve search results
// add it as a new object of 'SearchResults'
SQLPropertySearch search = new SQLPropertySearch(_place, _propertyType,
_askingPrice, _rooms, _livingSpace);
ArrayList<Integer> idResults = search.getPropertyIDlist();
SQLProperty property;
if(!idResults.isEmpty()) {
for(int i=0; i<idResults.size(); i++) {
property = new SQLProperty(idResults.get(i));
results.add(new SearchResults(
property.getPropertyID(),
property.getPropertyName(),
// and so on..
));
}
}
}
public static class SearchResults {
int propertyID;
String propertyName;
// and so on..
public SearchResults(int propertyID, String propertyName) {
this.propertyID = propertyID;
this.propertyName = propertyName;
// and so on..
}
// getter and setter
public int getPropertyID() {
return propertyID;
}
public void setPropertyID(int propertyID) {
this.propertyID = propertyID;
}
// and so on..
}
public ArrayList<SearchResults> getResults() {
return results;
}
}
In my XHTML-file I go through each entry of my ArrayList results.
It looks like this:
<ui:repeat var="res" value="#{PropertySearchBean.results}">
<p>#{res.propertyID}</p>
<p>#{res.propertyName}</p>
</ui:repeat>
I don't have an idea how to initialize the ArrayList, because first thing to do is the search itself, with the user input.
I am thankful for any kind of help!
You've removed the getters and setters from your example to improve readability. I'll provide one implementation here to ensure a common understanding (especially regarding the leading underscores).
public String getPlace() {
return _place;
}
public void setPlace(String place) {
this._place = place;
}
The property 'place' will be accessible within your view by using the value binding #{propertySearchBean.place}(see below).
Your code is meant to perform a search. Therefore you'll have to transfer user input from your XHTML file (view) to your managed bean. To do so you need to add a form to your view. Each search query parameter is bound to your bean using a specific value binding. Additionally the form contains a <h:commandButton> tag which finally triggers initialization of the result list.
<h:form>
<h:outputLabel for="place" value="Place:" />
<h:inputText id="place" value="#{propertySearchBean.place}" />
<!-- Additional fields -->
<h:commandButton action="#{propertySearchBean.initializeSearchResults}"
value="Search"/>
</h:form>
Note: You've used the following code in your example
<ui:repeat var="res" value="#{PropertySearchBean.results}">
Make sure that the first letter of your bean name is lower-case (propertySearchBean instead of PropertySearchBean). So this needs to be updated to
<ui:repeat var="res" value="#{propertySearchBean.results}">

Struts2: Updating the values of a "List Of Objects" inside a Map

There is an object ObjectA which has a list of ObjectB. There is a TreeMap inside the ObjectB. This TreeMap has a String as key and a List of another object ObjectC as value. This TreeMap and the list inside has been displayed on the jsp using the s:iterator and s:textfield and it is being displayed correctly. i.e. the "values" inside the s:textfield are correct. Now, the problem arises when the textfield is modified. How do we capture the modified values inside ObjectC in the action class? With the code given here, the key ("Key1") comes in the action but the value is null.
Java Code
public class ObjectA implements Serializable {
private Integer attr1;
private List<ObjectB> objB;
//...getters and setters....
public class ObjectB implements Serializable {
private Integer attr11;
private TreeMap<String,List<ObjectC>> allPlainFields;
// ...getters and setters....
public class ObjectC implements Serializable {
private Integer attr111;
public String attr112;
// ...getters and setters....
JSP Code
<s:iterator value="objA.objB" var="currentObjB" status="currentGroupStatus">
<s:iterator value="#currentObjB.allPlainFields" var="parentMap" status="headerStatus">
<s:iterator value="#parentMap.value" var="fieldList" status="fieldStatus">
<s:textfield name="objA.objB[%{#currentGroupStatus.index}].allPlainFields['%{#parentMap.key}'][%{#fieldStatus.index}].attr112"/>
</s:iterator>
</s:iterator>
HTML rendered:
<input type="text" id="review-act1_objA_objB_0__allPlainFields_'Key1'__6__attr112" value="Correct Value" name="objA.objB[0].allPlainFields['Key1'][0].attr112">
The object structure in the "VAriables" view of eclipse shows:
objA Object A (id=955)
objB ArrayList<E> (id=966)
elementData Object[10] (id=967)
[0] ObjectB (id=968)
allPlainFields TreeMap<K,V> (id=972)
comparator null
descendingMap null
entrySet TreeMap$EntrySet (id=979)
keySet null
modCount 1
navigableKeySet null
root TreeMap$Entry<K,V> (id=980)
size 1
values null
[1] ObjectB (id=969)
[2] ObjectB (id=970)
[3] ObjectB (id=971)
[4] null
[5] null
[6] null
[7] null
[8] null
[9] null
modCount 4
size 4
****In the Eclipse "Variables" view, the value for allPlainFields is**:** {Key1=}
EDIT(27-Feb-2013):
Tried this but didn't work. The values appear on jsp but when submitted, they don't come in action:
In Action class:
private TreeMap<String,ObjectCList> testTreeMap = new TreeMap<String,ObjectCList>();
//get,set and setting two keys in map "mykey1" and "mykey2"
In ObjectCList class:
private ArrayList<ObjectC> paramMdlList;
//default constructor, get, set
In JSP:
<s:form id="test-map" method="post">
<s:iterator value="testTreeMap" var="pMap" status="hStatus">
<li><label><s:property value="%{#pMap.key}" /></label>
<s:iterator value="%{#pMap.value.paramMdlList}" var="pList" status="innerStatus">
<s:textfield name="testTreeMap['%{#pMap.key}'].paramMdlList[%{#innerStatus.index}].attr111"/>
<s:textfield name="testTreeMap['%{#pMap.key}'].paramMdlList[%{#innerStatus.index}].attr112"/>
</s:iterator>
</li>
</s:iterator>
<s:submit value=" " type='button' id="btnh1" action="saveTreeMap">
<span>Save TreeMap</span>
</s:submit>
</s:form>
When the form is submitted, updateTreeMap method of the action is called. The map is printed as mentioned here :
public String updateTreeMap(){
for (Map.Entry<String, ObjectCList> entry : testTreeMap.entrySet())
{
System.out.println(entry.getKey() + "/" + entry.getValue());
}
return SUCCESS;
}
What is "printed" :
mykey1/
mykey2/
i.e. null values
The screen below shows values coming in jsp
According to your latest update. If you are using TreeMap Struts2 cannot correctly determine type of elements inside it. Change declaration of testTreeMap from TreeMap to Map.
private Map<String,ObjectCList> testTreeMap = new TreeMap<String,ObjectCList>();
Or annotate testTreeMap with com.opensymphony.xwork2.util.Element annotation to tell Struts2 what type are elements inside map.
#Element(value = ObjectCList.class)
private TreeMap<String,ObjectCList> testTreeMap = new TreeMap<String,ObjectCList>();
I've become curious and did other experiments.
I found out that neither Lists inside Lists, nor Maps inside Maps (and all the interpolations), declared as interface (List, Map), or as their implementations (ArrayList, HashMap, TreeMap) are correctly handled by the XWork Converter.
All the test cases have failed.
Maybe it's my fault, if so we really need some OGNL Experts here, because in the whole web there's nothing talking about this.
Then i tried what I was pretty sure that would have worked: encapsulating this informations in custom objects, in pure OOP way.
And it worked :)
Instead of
private ArrayList<ArrayList<String>> outerObjects;
you can use in your Action
private ArrayList<OuterObject> outerObjects;
/* GETTERS AND SETTERS */
public ArrayList<OuterObject> getOuterObjects() {
return outerObjects;
}
public void setOuterObjects(ArrayList<OuterObject> outerObjects) {
this.outerObjects = outerObjects;
}
the OuterObject definition:
/* ELSEWHERE IN YOUR PROJECT... */
public class OuterObject{
private ArrayList<InnerObject> innerObjects;
/* GETTERS AND SETTERS */
public ArrayList<InnerObject> getInnerObjects() {
return innerObjects;
}
public void setInnerObjects(ArrayList<InnerObject> innerObjects) {
this.innerObjects = innerObjects;
}
}
the InnerObject definition:
public class InnerObject{
String innerField;
/* GETTERS AND SETTERS */
public String getInnerField() {
return innerField;
}
public void setInnerField(String innerField) {
this.innerField = innerField;
}
}
the optional execute() method of your Action to test the preset values:
InnerObject innerObj1 = new InnerObject();
innerObj1.setInnerField("Inner Value 1");
ArrayList<InnerObject> innerObjArrayList = new ArrayList<InnerObject>();
innerObjArrayList.add(innerObj1);
OuterObject outerObj1 = new OuterObject();
outerObj1.setInnerObjects(innerObjArrayList);
outerObjects = new ArrayList<OuterObject>();
outerObjects.add(outerObj1);
the JSP:
<s:form>
<s:textfield name="outerObjects[0].innerObjects[0].innerField" />
<s:submit/>
</s:form>
(when iterating, simply use [%{#stat.index}] for Lists and ['%{#stat.index}'] for Maps)
The same solution is applicable for every kind of iterable structure (probably except the Guava stuff that needs the .create() method to be invoked).
Of course this is not handy in every case, in your example you have already a huge structure and this will almost double it, but it works, it's OOP, your OGNL will be clearer (because of names) and however seems to be the only way.
Note: the Classes must be real stand-alone Classes, not Inner Classes, another case where OGNL fails to autowire the objects.
Hope that helps
EDIT
What you need then, is only one more level:
change this:
private TreeMap<String,List<ObjectC>> allPlainFields;
to this
private TreeMap<String,ObjectX> allPlainFields;
and create an ObjectX containing a private field that is a List<ObjectC>.
It will work.

Categories