01 November 2005

Struts w/ Ajax and submitting editable dynamic content

I wanted to dynamically render a portion of a page using AJAX. I started by reading Sprinkle Some AJAX Magic in Your Struts Web Application by Paul Browne, but felt this fell short for a number of reasons:

  • I wanted to minimize the amount of Javascript I had to maintain for perform the round-trips

  • I wanted to use Struts actions and JSP to render the dynamic content

  • My dynamic content contains input elements which must be submittable and maintained across submits


I took a quick look at the prototype.js and decided to use it for performing the asynchronous stuff. It doesn't seem to have much documentation, but browsing the Javascript source helped. I found Using prototype.js v1.3.1

Modifying the main JSP page to dynamically include some content was easy. I had a select list which, when a new value was selected, would asynchronously load some data then present it in a table using displaytags. The values would be editable. Here is what it looked like:

<html:select property="configureApplicationId" styleid="configureApplicationId">
<html:optionscollection property="configureApplicationOptions">
</html:select>
<html:errors property="configureApplicationId">

<div id="configurationSettingsId">
</div>

<script type="text/javascript">
new Form.Element.EventObserver(
'configureApplicationId',
function(element, value) {
new Ajax.Updater(
'configurationSettingsId',
'myAction.do',
{parameters: Form.Element.serialize(element)}
)
}
);
</script>

This sets up an event listener on the change to the configureApplicationId which invokes an asynchronous HTTP post to myAction.do with the parameter configureApplicationId=value. On return, it replaces the content of the configurationSettingsId element with the result of the invocation.
The action is a regular Struts action which populates a form with the given parameter. The action forwards to a plain JSP page which renders the form content. Since the JSP page doesn't contain an <html:form> Struts tag, you have to add the name="formname" attribute to each <html:...> input element as so:

<html:hidden name="configureForm" property="configSettingsSerialized" />

Now when we submit the form after dynamically rendering a portion, the dynamic portion will disappear unless we take steps to re-render it. We could use some more javascript to do another asynchronous round-trip, but that usually has a noticable visual delay. I chose to render the dynamic portion in-line.
To do this we need to make available the same Struts ActionForm that is normally instantiated during the asynchronous round-trip and include the JSP page that is normally forwarded to during the asynchronous round-trip.

To do this, I added a method to the main page form which returned the ActionForm needed for the rendered portion and did a JSP include:

<div id="configurationSettingsId">
<c:set scope="request" var="configureForm" value="${mainForm.configureForm}" />
<jsp:include page="/path/to/jsp/myPage.jsp" />
</div>
<
Of course, for this to work, the main form must be able to populate the sub-form's values such that they can be rendered by the included JSP. That is simply a case of adding the appropriate setters to the main form, or including the same ability to setup the data as the other Action.

No comments: