I have created a javascript widget which lets users move options from one multi-select to another as input to a search. For example they can select one or many hardcoded options in an "allowed" select box and then click on an arrow to move the selected options into the "selected-for-this-search" select box. Just before the form is submitted, I also select all the options in all the "selected-for-this-search" boxes so that they are submitted.
My question is, can Struts support this? From the messages on this list it seems like multi-select box values need to be kept in an Array but I think mine need to be in a HashMap
[Craig McClannahan] In principle, it *should* still work with an array.
The key is that your setter method should take a String array as the arg:
public void setSelectResults(String selectResults[])
and Struts will then accumulate all of the options that were actually selected into a String array and call this setter for you. Because it's a new array, there is no requirement that the number of selected items be the same (or anything like that). Struts does not really care what
happens on the client side before the submit (which is where your JavaScript manipulations to implement the above takes place).
But what about when i get sent back to the form (perhaps from a validation error). How will the "selected" select box repopulate itself if all it has to go on are the Strings in the associated array?
[Craig McClannahan] The set of selected objects will be marked "selected" because Struts will call the corresponding property getter. Repopulating the list of all available options is going to have to be managed by your own code. Perhaps you can include a hidden variable containing all of the currently listed choices as part of your submit?
How do I set default values for radio and checkboxes?
[Craig McClannahan] I normally use boolean properties in my form beans for these. You can set them to appropriate defaults when the bean is first created (i.e. in the constructor) just like you can set all the String properties to defaults.
I just am starting out with Struts, and I have a question. I started with this syntax in my test jsp:
<html:form name="questionForm" action="insertQuestion">
<html:text property="questionDesc"/>
</html:form>
but I was getting this error:
javax.servlet.jsp.JspException: Must specify type attribute if name is specifiedI saw a reply from Craig McClanahan that states:
If you specify name here, you must also specify type. If you change this to:
<form:form action="regstep1.do"> The tag will be smart enough to figure out what form bean name you want, without having to specify it both here and in struts-config.xml.When I follow Craig's suggestion, I am unsure how "The tag will be smart enough to figure out what form bean name you want." Does it use the JSP name to link to the "path" attribute of the action tag, and link the name attribute of the action tag to the name attribute of the form-bean tag?
[Ted Husted] In general, you shouldn't need to specify the form name or type in the JSP at all. The form tag will lookup that up from the Struts config, according to where you are submitting the form (action path = action path).
<html:form action="/insertQuestion">
<html:text property="questionDesc"/>
</html:form>
<action path="/insertQuestion"
type="com.companyname.actionmap.InsertQuestionAction"
name="questionForm"
scope="request"
validate="false">
<forward name="success" path="/strutstest.jsp"/>
</action>
should be all you need.
We moved our JavaServer Pages below WEB-INF, but now it can't find the cascading style sheets.
[Ted Husted] Most containers allow JavaServer Pages and templates to be kept before WEB-INF. However, the style sheets and images are retrieved by the browser, and need to be in the client-accessible portion of your application (above WEB-INF).
We need something like nested forms, because we have actions for a list of objects and actions for a single one of these objects.
HTML itself doesn't allow anything like nested forms, so you would have to find a way to simulate the same effect.
The dbforms framework (dbforms.org) does some interesting things in this view.
In the end, though, HTML demands one form, one submit.
What's the best way to call the reset() method on a Form Bean? (so that all the values on a form are always the default ones)
[Ted Husted] The ActionServlet usually does this for you when it is needed. In general, your ActionForm beans should be in request context, and so you would start out a fresh one with a new request cycle. If validation fails, the ActionForm beans carries the data back to the input form so the user can correct it. Struts does try to recycle ActionForm beans when it can, and will calls reset() on a pre-existing bean before populating it again from the request. You generally should not need to call this yourself.
It is important to remember that ActionForm beans are transitory and should only be used to ferry data between the HTML form and the Action, where the data can be transferred to a persistent object.
Have you had any problems with struts defining weird sizes on your table rows? My row heights are unusually high, and I haven't set the size.
[Ted Husted] Struts doesn't write the HTML for tables, and could not affect the sizes of your table rows, except to the extent that the browser adjusts the row sizes to fit the instant data.
The Struts custom tags (like all others) simply substitute standard HTML for the custom tags on their way to the browser, and leaves everything else alone. You can view the source to see what the browser sees. Struts isn't otherwise involved in how your page renders
If I want to use <bean:define> to retrieve one property of current form bean as a JSP bean (an attribute accessible to the remainder of the current page), how can specify the attribute name of the form bean? I have tried the name from struts-config.xml where I define the form bean, it is still saying: "No bean found for attribute key xxx"(the name appearing in the xml file). How do all the html form tags know the form bean where they find "their properties"? Finally I have to use <jsp.useBean> to introduce the form bean before the defining.
[Ted Husted] The form bean is created by the html:form tag, or by an Action class, under the attribute name given in the config file. Are you looking for it before it is created?
But, given that the flow has passed the html:form tag, or returned from the action, and given something like
<form-bean name="logonForm" type="org.wxxi.gavel.action.LogonForm"/>
then
<bean:define id="username" name="logonForm" property="username"/>
should expose the property logonForm.getUsername() as a scripting variable for the page.
Of course, this is only needed for scriptlets or other non-Struts resources. The Struts tags will find the bean automatically.
When creating a table row, is there a way to avoid having to specify the context name in the path for images as shown below:
<td background="<context-name>/images/table_top_chip.gif" width="30" ></td>
<html:base/> does not seem to be sufficient for this purpose. Also, using relative path is not preferred for the application since the pages can move around.
There aren't any Struts tags for writing tables, but one workaround would be to try a standard JSP expression, like
<td background="<%=request.getContextPath()%>/images/table_top_chip.gif%>"
Struts is designed to work well with others, so any table tags you found elsewhere would probably plug right in.
I need to use an image for a button on a form. What methods should I code inside my LoginForm corresponding to those two buttons? How to code the properties (loginButton, forgotPasswordButton). I'm more concerned about whether a particular button is clicked rather then their co-ordinates.
[Ted Husted] If you use standard HTML buttons, the property and value for the button is passed as a parameter (property=value), that you can retrieve from the request context. A typical approach here would be to give each button the same property but
different values, like
<html:image page="/images/login.gif" alt="Login" property="login" value="havePassword" />
<html:image page="/images/forgotpassword.gif" alt="Forgot Password" property="login" value="forgotPassword" />
Then in the action method, you would use a pattern like
string loginType = request.getParameter("login") ;
if loginType.equals(""forgotPassword") {
// forward to forgotPassword page
}
if loginType.equals("havePassword") {
// validate password
}
Though, in an i18n environment, this is more difficult, since the value is also the button's label. If the labels are localized, you would have to check the Application Resources for the user's locale to get the label's key, and use that in your logic instead.
If you use image buttons (html:image), then the browser transmits the x and y coordinates of the mouse click. This was decided by the HTML specification, and there is nothing we can do about it :-(. All we know is what the browser sends, and all the browser will send is the x and y coordinates.
However, if your requirements permit Javascript, an alternative that works in both cases is to attach a script to an onclick event that sets a hidden property in your form. Your Action can then check for that to see which task was requested.
<html:hidden property="task">
<html:image src="${whatever}" onclick="javascript:action=logon;"/>
<html:image src="${whatever}" onclick="javascript:action=forgot;"/>
then in your Action
string loginType = request.getParameter("task") ;
if loginType.equals("task") {
// forward to forgotPassword page
}
if loginType.equals("task") {
// validate password
}
If you will be using this strategy, it's a good idea to define a subclass for your ActionForms with a task property (e.g. TaskForm), and then subclass your others from that.
http://www.mail-archive.com/struts-user@jakarta.apache.org/msg06601.html
http://www.mail-archive.com/struts-user@jakarta.apache.org/msg06197.html
http://www.mail-archive.com/struts-user@jakarta.apache.org/msg15597.html
http://www.mail-archive.com/struts-user@jakarta.apache.org/msg15727.html
How do you make the errors shows up on the generated page? What should I put at the place of '?' in the above add method
LogonForm.java
public ActionErrors validate(ActionMapping mapping, HttpServletRequest request)
{
if(password == null || !password.equals("password"))
{
ActionErrors aes = new ActionErrors();
ActionError ae = new ActionError("Please type the
valid password");
aes.add("?", ae);
return aes;
}
return null;
}I saw one of the postings talking about saveErrors method. It belongs to Action Object, and I how do I get reference to action object from the validate method?
[Ted Husted] The ActionForm.validate() method is called by the ActionServlet. If it does not return null, the ActionServlet will automaticaly save the errors and return to the input page specified in the ActionMapping.
The first parameter to ActionErrors.add() is the name of the invalid property. For a non-specific error, the constant ActionErrors.GLOBAL_ERROR is provided.
The first paremeter to the ActionError (singular) constructor is suppose to be a key in your Application Resources file. By default, if this key does not exist, then nothing is displayed by the html:error tag. You can change this behaviour in the web.xml, by setting null to false.
<init-param>
<param-name>null</param-name>
<param-value>false</param-value>
</init-param>
The messages in the Application Resource file can take up to four replacement parameters. The ActionError constructor takes several signatures so that you can specifiy the key and the needed parameters. To display an arbitrary message, add a line like this to your file:
errors.dynamic = {0}
You could then use something like
actionErrors.add(ActionError.GLOBAL_ERROR, new ActionError("errors.dynamic","Please type the
valid password");
However, burying message strings in class files this way is a poor design strategy. They should either be gathered together in a static class, or placed in the Application Resources file.
[Jeff Trent] Attached, you will find a couple of new tags I wrote to extend the HTML tag library that comes with struts that will allow you to write grid-style JSP code that looks like the following:
<% int i = 0; %>
<logic:iterate id="item" name="inventoryForm" property="items">
<tr>
<td></td>
<td class="smallFont"><html:gridtext name="item" property="name" index="<%= i %>" maxlength="nameMaxSize"
size="30"/></td>
<td class="smallFont"><html:gridtext name="item" property="description" index="<%= i %>"
maxlength="descriptionMaxSize" size="50"/></td>
<td class="smallFont" align="center" valign="center"><html:gridcheckbox name="item" property="containerFlag"
index="<%= i %>" value="Y"/></td>
</tr>
<% i++; %>
</logic:iterate>
Here is a synopsis of what I did:
(1) added two tags called "gridtext" and "gridcheckbox" that are based on html:text and html:checkbox respectfully. Note: All of the others (ie. hidden, radio, select, etc.) need to eventually be handled in a similar fashion but this is all I needed for right now. I'll
keep you posted on the rest as I create new gridXXX tags. Note 2: this code has in no way been endorsed by Apache / Struts.
Therefore, buyer beware! This code could easily break in future releases to struts.
(2) gridtext can now optionally take an index field. If no index is given, the behavior of gridtext degenerates to your vanilla html:text tag. If index is specified, it will use the correct semantics for referring to your iterated item in the collection.
(3) gridtext can now optionally take a bean property name in place of a literal value for the maxlength. I felt this is more convenient than the alternative (using scriptlet). To use this style, make sure your underlying bean object (in the above case its the
"item" bean), has a member called getNameMaxSize() which returns a string. Note: "size" still requires a literal, the bean will not be consulted if you use a property name instead.
(4) gridcheckbox. Well, let me tell you - a lot was changed in gridcheckbox! I began by copying the code for checkbox tag over to gridcheckbox. I changed all private declarations over to protected, removed the 'final' on the class declaration, and fixed a few
bugs along the way. Bugs included: (a) wrong target package, (b) used new RequestUtil to get bean value (not really a bug), (c) handle the value parameter correctly (in the 3.2.1 release, the value clause was being confused with the data coming back in the
form post. gridcheckbox will really take your user-defined value and put it in the "value" attribute. Also, "checked" logic will use your "value" attribute and compare to datavalue posted from the form. If value is not specified, the default value will be "true").
(5) gridcheckbox can now optionally take an index field just like gridtext...
Also, note: this has not been qa'ed properly. I wrote the code in under an hour and wham, here you go. If there are bugs you find in the indexing logic, feel free to let me know.
A few words on checkboxes:
As many of you know, if a form is posted and no checkboxes are checked, html (and therefore Struts) will not fire the setter(s) for those checkbox properties. To solve this problem, you should depend on your reset() method on your form to clear out all of the
checkbox values each time reset() is called.
To incorporate this code, do the following:
(1) make a backup of your struts-html.tld
(2) overwrite your project-level struts-html.tld with the one attached.
(3) put GridTextTag.java & GridCheckboxTag.java in your project.
(4) send me email to let me know how you fared with them ;^) I'd like to hear from you.
Can the value to a multibox tag be dynamic?
[Craig McClanahan] Yes, the <html:multibox> tag has been enhanced so that you can determine the value to be returned to the server dynamically. Thus, you can specify either:
<html:multibox property="myprop" value="myvalue"/>
or (for example)
<html:multibox property="myprop">
<bean:write name="mybean" property="valueprop"/>
</html:multibox>
Can we access members of an array like <form:select property="array[i]"> in a for loop... It works if I use constants but not expressions.. property="array[0]" is fine but not array[i] or array[<%=i>] for the property attribute of form:select. Any clues???
[Craig McClanahan] The property accessor syntax in Struts 1.0 supports only constants as array subscripts.
You might consider using some sort of runtime expression instead, to dynamically calculate the property to be retrieved. Assume that "i" is an integer variable, and you are inside a loop:
<form:select property='<%= "array[" + i + "]" %>'/>
Note: indexed properties are now available in the nightly build and should be part of Struts 1.1 and later.
How do radio buttons work/?
[Craig McClanahan] The idea behind a radio button is that you have a String field that has one of a fixed set of values, and you want to render one radio button for each of them.
Let's assume that your bean property "gender" can have property values "Male" or "Female". You might display them like this:
<html:radio property="gender" value="Male">
<bean:message key="gender.male"/>
</html:radio>
<html:radio property="gender" value="Female">
<bean:message key="gender.female"/>
</html:radio>
A particular radio button will be selected if the value returned for this property matches the "value" attribute of that button's <html:radio> element.
In the example above, we're also looking up the actual text of the strings displayed on the HTML page in a message resources bundle, so that it is suitably internationalized.
I usually need 'wrap="virtual"' as an attribute for my textareas. It isn't part of the HTML spec, but it's supported in both Netscape and IE, and it's pretty useful.
[Ted Husted] The Struts Action works through introspection. It doesn't know (or care) whether the value in a form was rendered by a Struts custom tag or not. This means if you use a standard textarea tag in your form, and give it a name that matches a property in your form bean, Struts will use that attribute just as if it had been rendered by a html:textarea tag. After all, this is all the Struts html:textarea tag does.
To seed the textarea from the form bean, you could code something like:
<textarea name="article" rows="15" cols="60" wrap="soft"><bean:write
name="formName" property="article"/></textarea>
And everything else will be automagical again.