struts2: param tag doesn't use conversion - tags

Problem
In my struts2 application I have JSP page where the list of book is displayed. Each book has an id and the name. Also I want to place a link "edit" alongside any book, which can be edited by current user. This "edit" link should invoke corresponding action, passing book id as parameter to it. So I implemented this page using the code:
<table>
<s:iterator value="books" status="stat">
<tr>
<td><s:property value="id"/></td>
<td><s:property value="name"/></td>
<td>
<s:if test="canEdit[#stat.index]">
<s:a action="editBook">edit
<s:param name="bookId" value="id"/>
</s:a>
</s:if>
</td>
</tr>
</s:iterator>
</table>
Book id is not of a basic type, I use custom class for it. So I decided to implement my own converter.
And here comes the problem: in the code above converter is used only when evaluating tag <s:property value="id"/> but it is not used to evaluate <s:param name="bookId" value="id"/>. Instead toString() method of book id class is used. Why <s:param> doesn't use my converter? How do i force it to do so? Or maybe there is another way to pass book id as parameter in link?
Some (probably useless) details
To configure converter I placed xwork-conversion.properties inside my /src/main/resources/ folder with the following contents:
my.app.Id = my.app.struts.IdConverter
my.app.Id is an abstract class which is extended by book id class.
The result of rendering of the JSP for a single-entry list is the following:
<table>
<tr>
<td>8</td>
<td>Book 01</td>
<td>edit</td>
</tr>
</table>
id%288%29 is an escaped version of string id(8) which is the result of toString() method (defined in base abstract class my.app.Id) for id with int value of 8.

The <s:param> tag is meant for "simple" parameters. In this case, you may be able to just set a temporary value using <s:property> if that does the conversion you expect.
It uses the toString of the book id because that's what a getId() will print out when rendered, just as if you did a System.out.println(book.getId()).

Found corresponding issue with explanation of problem:
http://jira.opensymphony.com/browse/WW-808
In a few words the difference is between specifying <param value="myValue"/> and specifying <param><property value="myValue"/></param>. Conversion is used only in second case.

Related

How to unescape a default value of an input field in Thymeleaf?

I am passing a model object to a view to be used in a form. Nevertheless I need to unescape one of the parameters and Springboot is throwing an error when I use #uris.unescapePathSegment because it can not find the relevant bean
the code
<tr>
<td colspan="8"><textarea rows="15" cols="150"
th:field="${#uris.unescapePathSegment(excerpt.comments)}" /></textarea></td>
<td class="validation-comments" colspan="5"
th:if="${#fields.hasErrors('excerpt.comments')}"
th:errors="${excerpt.comments}"></td>
</tr>
the error
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name '#uris' available as request attribute
How should I solve this? Is it possible to unescape the parameter in Thymeleaf before I use it in the field or should I unescape it in the controller?
A th:field can't be an expression. It can only be a selection expression *{...} (see the docs on inputs) that points to a single field on your form-backing bean (th:object). If you actually do need to unescape it, it should be already unescaped by the time you get to *{excerpt.comments}.

How to access a session ArrayList by index in a form?

I have a session variable which is of type ArrayList.
In the jsp page I need to access it by index to create a form dynamically, but after I submit the form I found out that the session ArrayList's elements values didn't change.
Here it is what I've tried on my JSP page (I use struts2 Framework):
<s:iterator value="anotherArray" status="RowsIterator">
<tr>
<td>
<s:iterator value="actionOptionsArray" status="iter">
<s:radio
name="#session.chosenActionsArray[%{#RowsIterator.index}]" <!-- The concerned line -->
list="%{actionOptionsArray[#iter.index]}"
value="#{actionOptionsArray[0]}"
theme="simple" />
<br>
</s:iterator>
</td>
<!-- other fields-->
</tr>
</s:iterator>
anotherArray and #session.chosenActionsArray have the same size.
I guess I iterate it wrongly, but in my case iterating it by index is an obligation.
Thank you a lot in advance :)
You need to access the session via an action, the session is accessible from the jsp but not directly from the outside world in this way.
Have the action you are submitting the form to implement SessionAware. I would create a getter/setter for an ArrayList along with proper validation and then move those values into into the session via the execute method. I'm not a fan of exposing your session directly to the outside world (providing a setter for the session in your action)... if you do this you need to be aware that you may have given a malicious user access to things you might not have expected.
Edit
Suppose you have an ArrayList of ArrayList of String called "matrix" in your action... you can iterate the properties via:
<s:iterator value="matrix">
<s:iterator>
<s:property/>
</s:iterator>
</s:iterator>
The outer iterator, iterates over "matrix" which pushes each instance to the top of the value stack. The inner iterator will use what is at the top of the stack by default same with the property tag. Placing tr's and td elements in the right place an you could render a table.
To generate the right name attribute for input elements (possibly hidden ones) you would want them in the form matrix[i][j] where i and j are integers and would define an appropriate matrix. Using status attribute of iterator as you've done would be a good way to generate the indexes.

Spring MVC 3 - custom labels in <form:select>

I'm creating a form in which user will be able to choose (among the others) the factory of a product.
Each factory is identified by and ID and has specific address.
I want to use custom label in following code:
<form:select items="${factories}" path="factory" itemValue="id" itemLabel="..."/>
At first i tried using Spring Formatter functionality (org.springframework.format.Formatter interface), but when I did this, and when I removed "itemLabel" attribute to have it displayed automatically via Formatter):
<form:select items="${factories}" path="factory" itemValue="id"/>
But then It wasn't selecting proper value if it was set (in case of editing).
Then I tried to:
<form:select path="factory" itemValue="id">
<c:forEach ...>
<form:option value="${factory.id}" label="${factory.address.city} ${factory.address.street}"
</c:foreach>
</form:select>
But as in earlier solution spring was not selecting proper value that was set in model.
My question is:
Is it possible to format entity in a way, that form:select works properly, when a value of select field is not the same as its label.
I had the same problem, the trouble is the form:option value and label maps directly to a property rather than having the flexibility to specify jstl.
So you have to hand crank the html, to something like:
<select name="factory">
<c:forEach var="factory" items="${factories}" >
<option value="${factory.id}" label="${factory.address.city} ${factory.address.street}"/>
</c:forEach>
</select>
Spring will pick up the 'path' based on the name attribute('factory' in this case), as it will try and map to the model object you are using automatically.
Another way is to add another field to the model to concatenate and format label as you wish. And if its an #Entity object then make the field #Transient.
You can also override toString() method in Factory class
#Override
public String toString() {
return "desired string";
}
Then in your jsp
<form:select items="${factories}" itemValue="id" path="factory"/>

customizing size of form fields created using field.custom.widget

I used the following code to generate a form in attached image.
Is it possible to change the size of the fields in the form.
I want to decrease size of input field of Estimated time and the dropbox field to the right of it
{{=form.custom.begin}}
<table>
<table><tr>
<td><b>Type :</b></td><td><div>{{=form.custom.widget.type}}</div></td>
</tr><tr>
<td><b>Title :</b></td><td><div>{{=form.custom.widget.title}}</div></td>
</tr><tr>
<td><b>Description :</b></td><td><div>{{=form.custom.widget.description}}</div></td>
</tr><tr>
<td><b>Estimated Time :</b></td><div'><td>{{=form.custom.widget.estimated_time}}{{=form.custom.widget.estimated_time_unit}}</td> </div>
</tr>
<tr>
<td></td><td><div align='center'>{{=form.custom.submit}}</div></td>
</tr>
</table>
{{=form.custom.end}}
Yes. You can and there are many ways.
The recommended way is to look at the generates JS. You will find it follows a naming convention described in the book. You can use CSS to change the look-and feel of every widget.
input[name=estimate_time] { width:50px }
Similarly you can use JS/jQuery (I would recommend you do this in the view).
jQuery(document).ready(function(){ jQuery('input[name=estimate_time]').css('width','50px');});
You can also use jQuery-like syntax in python in the controller:
form.element('input[name=estimate_time]')['_style']='width:50px'

Can I append an Ajax requestXML object to my document tree all in one go?

Greetings.
Here is an XML object returned by my server in the responseXML object:
<tableRoot>
<table>
<caption>howdy!</caption>
<tr>
<td>hello</td>
<td>world</td>
</tr>
<tr>
<td>another</td>
<td>line</td>
</tr>
</table>
Now I attach this fragment to my document tree like so:
getElementById('entryPoint').appendChild(responseXML.firstChild.firstChild);
But instead of being rendered as a table, I get the following text:
howdy! helloworldanotherline
The same result occurs of I replace firstChild.firstChild with just firstChild.
It seems like I'm just getting the nodeValues, and all of the tags are stripped out?!
Am I fundamentally misunderstanding what the responseXML object is supposed to represent?
This works, BTW, if I take out the 'root' tags, and set innerHTML to responseText.
Can someone please enlighten me on the correct way to use responseXML?
You get the text instead of a table, because you use pure DOM for manipulations and your response XML doesn't have the namespaces declarations. So when appending an XML element browser doesn't know whether your "table" tag is from HTML, XUL, SVG or else from.
1) Add namespace declaration:
<table xmlns="http://www.w3.org/1999/xhtml">
2) Instead of directly inserting a reffered XML DOM Element, you should first import that node into your HTML DOM Document:
var element = document.importNode(responseXML.firstChild.firstChild, true);
document.getElementById('entryPoint').appendChild(element);
Hope this helps!
You can create an element at the position you want to insert and than do
element.innerHTML = request.responseText