Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Problems using netui:form tag with a repeater tag

101 views
Skip to first unread message

Paul Merrigan

unread,
Feb 26, 2004, 10:27:04 PM2/26/04
to

We're trying to use a repeater tag inside of a netui:form, with a netui:textBox
tag so we can enter data and have it hit a form bean for validation, but when
we try to display the data all we see is [Ljava.lang.String;@1421641 in each field.

We essentially copied the code explaining how to do this from the Workshop help
files into our code to test it out and are getting this strange result. Also,
each of the three fields that this repeater renders has the same name in the HTML;
they don't even appear to be recognizing the array nature of the form field.

The workshop link to the help we're following is:

http://support.bea.com/application?namespace=metrics&origin=ask_bea_answer.jsp&event=wildcard.metrics&portletname=AskBEA&ret_url=%2Fapplication%3Fnamespace%3Daskbea%26origin%3Dask_bea_answer.jsp%26event%3Dlink.view_answer_page%26page%3Dhttp%3A%2F%2Fe-docs.bea.com%2Fworkshop%2Fdocs81%2Fdoc%2Fen%2Fworkshop%2Fguide%2Fnetui%2Fguide%2FconComplexDataSets.html%23Repeater

Here's what our JSP looks like:

<%@ page language="java" contentType="text/html;charset=UTF-8"%>
<%@ taglib uri="netui-tags-databinding.tld" prefix="netui-data"%>
<%@ taglib uri="netui-tags-html.tld" prefix="netui"%>
<%@ taglib uri="netui-tags-template.tld" prefix="netui-template"%>
<netui:html>
<head>
<title>
Web Application Page
</title>
</head>
<body>
<netui:form action="validateStringArrayForm">
<netui-data:repeater dataSource="{pageFlow.myStringArray}">
<netui-data:repeaterHeader>
<ul>
</netui-data:repeaterHeader>
<netui-data:repeaterItem>
<li>
<netui:textBox dataSource="{actionForm.name}" defaultValue="{container.item}"
/>
</li>
</netui-data:repeaterItem>
<netui-data:repeaterFooter>
</ul>
</netui-data:repeaterFooter>
</netui-data:repeater>
<netui:button type="submit" value="submit"></netui:button>
</netui:form>

</body>
</netui:html>

The HTML renders as:

<html xml:lang="en" lang="en" xmlns="http://www.w3.org/1999/xhtml"><head>
<title>
Web Application Page
</title>
</head>
<body>
<form name="buildStringArrayFormForm" method="post" action="http://localhost:7001/ctdportal/portlets/test/validateStringArrayForm.do?">

<ul>

<li>
<input type="text" name="{actionForm.name}" value="[Ljava.lang.String;@1421641"/>
</li>

<li>
<input type="text" name="{actionForm.name}" value="[Ljava.lang.String;@1421641"/>
</li>

<li>
<input type="text" name="{actionForm.name}" value="[Ljava.lang.String;@1421641"/>
</li>

</ul>

<input type="submit" value="submit"/>
</form>

</body></html>

We're just starting to convert a major portal application from 4.0 to 8.1 and
we'll have a few occassions to use this so any help would be greatly appreciated.

Eddie O'Neil

unread,
Feb 27, 2004, 8:19:24 AM2/27/04
to Paul Merrigan
Paul--

The dataSource attribute on the textBox is used *both* as the source of data for the text box
when the JSP renders and as the destination for the POST value when the page is submitted. If what
you'd like to do is update the array items in the {pageFlow.myStringArray} property, you'll want to
change the textBox's dataSource to "{container.item}". Then, the names that will be written into
the rendered HTML will look something like this:

> <li>
> <input type="text" name="{pageFlow.myStringArray[0]}" value="First String"/>
> </li>
>
> <li>
> <input type="text" name="{pageFlow.myStringArray[1]}" value="Second String"/>
> </li>

and so on. The "{container.item}" binding context is the what causes the repeater to add the array
indices to the {pageFlow.myStringArray} expression.

Another thing you might consider is to expose the myStringArray property on an action form so
that validation can be run when the form POSTs to the "validateStringArrayForm" action. To do this,
the JSP might look like:

> <netui-data:repeater dataSource="{actionForm.myStringArray}">


> <netui-data:repeaterHeader>
> <ul>
> </netui-data:repeaterHeader>
> <netui-data:repeaterItem>
> <li>

> <netui:textBox dataSource="{container.item}" defaultValue="Some Default Value"/>


> </li>
> </netui-data:repeaterItem>
> <netui-data:repeaterFooter>
> </ul>
> </netui-data:repeaterFooter>
> </netui-data:repeater>

Then, the expressions in the rendered HTML will look like "{actionForm.myStringArray[#]}". You
might consider using JPF scoped forms so that the values POST back into the same form instance from
which they were read. This avoids needing to use the form's reset(...) method to re-create the array.

One thing to note on the textBox's defaultValue -- this will only render if the dataSource's
expression evaluates to "null". Otherwise, the value of the dataSource renders. In your case right
now, is the property referenced by {actionForm.name} a String[]? If so, that would explain why
you're seeing [Ljava.lang.String;@1421641 in each field -- the value of the dataSource is basically
toString()'ed when the tag renders.

Hope that helps. Let me know if you've got more questions.

Eddie

Paul Merrigan

unread,
Feb 27, 2004, 10:49:15 AM2/27/04
to

Thanks for the information. Being new to 8.1, we're still earning the form processing/JPF
pieces.

I'm now able to get the form to display with the proper indexes showing in the
name fields, but when I change data only the first item ends up in the form that
is received by the validating action method. I'm not sure if there is something
special I'm supposed to be doing, so any advice here would be appreciated.

You mentioned a reset method on the form, but I'm not sure how or when to call
this plus I don't know where to get the ActionMapping object to pass in.

Lastly, you mentioned using a JPF scoped form so I don't have to deal with the
reset issue. I'm not clear on how I'd make use of it in the action methods since
they're already receiving a form as input.

Again, thanks for your help on this. We were confused and stuck and this is giving
us some direction.

Eddie O'Neil

unread,
Feb 27, 2004, 1:51:04 PM2/27/04
to Paul Merrigan
Paul--

No problem; that's what the newsgroups are for. :)

First, the reset method is something that the Struts ActionForm class provides. The JPF
framework will call that method for you, so there's no need for you to provide the ActionMapping.

Here's what I think is going on in your case. In Struts, all action forms are scoped to the
session by default. In page flow, they are scoped to the request, unless you annotate an action
otherwise. Given this, the form that your action receives is going to be a new instance on every
request. The problem is that if you have an array inside of the form, it needs to be re-initialized
to the correct size in order to have non-null array items into which to put the POST data. When
expressions update bean properties on POST, the framework doesn't create these objects for you.

The reset(...) method on the action form is a good place to do this, but in the case of an array
(or List, etc), you often don't know how many items were in the array that rendered so it's hard to
create an array of the right size.

The solution is to use a "Page Flow scoped form", which means that the same instance of the form
that was used to render the JSP is used when the POST data is processed. To use a JPF scoped form,
you'd do something like this in your .jpf file:

:::::

// declare an instance of the form to use between several actions
private StringArrayForm myStringArrayForm;

/**
* This action will show the page that contains the repeater / form / textBox tags.
*
* @jpf:action
* @jpf:forward name="success" path="yourRepeaterForm.jsp"
*/
public Forward showRepeaterForm()
{
myStringArrayForm = new StringArrayForm();
... populate the array here ...

// passing the form here tells the <netui:form> tag to use this form for the "actionForm"
// binding context.
return new Forward("success", myStringArrayForm);
}

/**
* This action handles the POST data that is submitted into the String[].
*
* Annotate the "action" to specify the field name of the form to use for this action.
* This annotation attribute is the key to using a JPF scoped form. When
* the page POSTs, this form should contain the values that were entered
* in the text boxes. To make sure that this is true, just forward back to your
* repeater/form/textBox page and make sure that the values match what you expect.
*
* @jpf:action form="myStringArrayForm"
* @jpf:forward name="success" path="some.jsp"
*/
public Forward validateStringArrayForm(StringArrayForm form)
{
... your action code here ...
}
:::::

I think that the reason you're seeing only the first item in the array updated is probably
because the getter for the action form's myStringArray property initializes the first element of the
array when it is called but the array is null. Is this code in that method?

There is a NetUI tag that could help debug what is going on here; the
<netui:bindingUpdateErrors/> displays errors that occur when data binding is handling POST data.
This might help debug your pages at development time.

Let me know if something isn't clear.

Hope this helps...

Eddie

Paul Merrigan

unread,
Feb 27, 2004, 2:33:35 PM2/27/04
to

Thanks Eddie! That nailed it.

Now the only outstanding issue is how to display the validation errors on each
line that has a problem. Using the normal methods doesn't seem to work. Any
suggestions on how to flag only the errored lines while using a repeater?

Eddie O'Neil

unread,
Feb 27, 2004, 3:14:40 PM2/27/04
to Paul Merrigan
Paul--

Glad that worked.

The error question is an interesting one. You can try using the <netui:error> tag to display a
single error next to the text box, but I think that will display the error for every repeated line.

Certainly, you could start with the <netui:errors> tag which will render the error report in one
block.

Will have to look into if it's possible to use the <netui:error> tag this way.

John Rohrlich

unread,
Mar 8, 2004, 4:12:53 PM3/8/04
to
Paul,

I've worked out a way to display errors in the repeater in the way you want.

In the page flow code you add a validate method to your form and for each
error you create an ActionError where the key is the array index. By using
the array index each error generated is tied to a particular element.

Like this :

public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request)
{
ActionErrors errors = new ActionErrors();

for (int i = 0; i < items.length; i++)
{
if (items[i].getItemName() == null ||
items[i].getItemName().equals(""))
{
errors.add(String.valueOf(i), new
ActionError("requiredError"));
}

if ((items[i].getItemName()).length() > 10)
{
errors.add(String.valueOf(i), new
ActionError("maxLengthError"));
}
}
return errors;
}

In the jsp code you use the netui:error tag to display each error and by
using the index as the value attribute you are linking each error to the
element that is responsible for the error. Unfortunately the value must be a
string and you need some ugly code to do the conversion.

Here is the critical section of jsp code (note the comment):

<netui-data:repeaterItem>
<!-- Use container.index to have independent errors for each
row. -->
<!-- Because netui:error wants a string for "value" and
because -->
<!-- the expression language doesn't support type
conversion -->
<!-- we will use getData and scriptlet to do the
conversion -->
<netui-data:getData resultId="currentIndex"
value="{container.index}"/>
<%
Integer currentIndex =
(Integer)pageContext.getAttribute("currentIndex");
String theIndex = currentIndex.toString();
%>

<tr valign="top">
<td><netui:textBox
dataSource="{container.item.itemName}"></netui:textBox></td>
<td><netui:error value="<%= theIndex
%>"></netui:error></td>
</tr>
</netui-data:repeaterItem>

I have attached the .jsp, Controller and my Message.properties file so you
can run the example. Put the properties file in WEB-INF/classes.

- john

"Eddie O'Neil" <ekoneil...@bea.com> wrote in message
news:403FA53...@bea.com...

developer

unread,
Jun 17, 2004, 6:40:16 PM6/17/04
to
Eddie, You mentioned in earlier thread:

"I think that the reason you're seeing only the first item in the array updated is probably
because the getter for the action form's myStringArray property initializes the first element of the
array when it is called but the array is null. Is this code in that method?
"

1. Does it mean that, to get rid of the above issue, we need to initilize the 'myStringArray', which is there in the static form class, to exact size (equal to no. of rows in the form we wanted to display in jsp)?

. My scenario is exactly similar to the problem mentioned in the original message. I am trying to use the JPF scoped forms. The forms setter method initilizes only the first element of the array if it is null or array length=0. In this case, i am getting ArrayIndexOutOfBoundsException first time. Subsequently it is working fine!! Kindly let me know what i am missing here?

0 new messages