In the resent times I have been reading some interesting articles about AJAX and wanted to some examples using struts with AJAX.
1. Dependent Select Boxes
2. Suggest Text Field
The examples above assume that the user have a fair understanding about XMLHttpRequest and JavaScript. In the examples I will be using prototype.js Javascript Framework and script web 2.0 javascript.
prototype.js has all the utility functions to make an XMLHttpRequest to server and srcipt.aculo.us as the utility methods for effects and suggest layer generation and much more if you want to use.
Dependent Select Box:
Let‘s take a classic example of Address Form in a JSP page. Upon selecting the State from the Dropdown List corresponding City‘s needs to be populated in the city dropdown list. The question still remains why should I use AJAX? Well, the advantage of using AJAX is it will not reloaded or refreshed the complete page it will reload just the dependent dropdown list with values that are comming from the server.
addressForm.jsp is a simple JSP page which has a HTML Form and Form elements. In the JSP documentation for porototype
The AJAX request here is expecting an XML file with a list of LabelValueBean objects. page I have included prototype.js file as script include. Here is the JSP page looks like
<code>
<html>
<head>
<title>Address Form</title>
<!-- include the AJAX JS File -->
<script src="js/ajax/prototype.js" type="text/javascript"></script>
<SCRIPT LANGUAGE="JavaScript">
<!--
/*
onChange event of the dropDownList will calls this function
which has AJAX call to Struts Action class
@param: dropDownList object (this)
@param: URL or Struts Action
*/
function depedentDropDown(obj, url){
var stateValue = $F(obj)
var pars = "state=" + stateValue;
var myAjax = new Ajax.Request( url, {method: 'get', parameters: pars,
onComplete: showResponse});
}
/*
Upon completing the request the AJAX will call this method
which is responsible for loading the depedent list from the XML
*/
function showResponse(originalRequest)
{
var list = document.getElementById('city');
var xmlString = originalRequest.responseXML;
var items = xmlString.getElementsByTagName('labelValueBean');
clearList(list);
if (items.length > 0)
{
for (var i=0; i<items.length; i++)
{
var node = items[i];
var value="";
var label="";
if(node.getElementsByTagName("label")[0].firstChild.nodeValue){
value = node.getElementsByTagName("label")[0].firstChild.nodeValue;
label = node.getElementsByTagName("value")[0].firstChild.nodeValue;
}
addElementToList(list, value, label);
}
}
else
{
addElementToList(list, "", "-- Select is Empty --");
}
}
/**
remove the content of te list
*/
function clearList(list)
{
while (list.length > 0)
{
list.remove(0);
}
}
/**
Add a new element to a selection list
*/
function addElementToList(list, value, label)
{
var option = document.createElement("option");
option.value = value;
var labelNode = document.createTextNode(label);
option.appendChild(labelNode );
list.appendChild(option);
}
//-->
</SCRIPT>
</head>
<body>
<form method="POST" action="">
<fieldset style="padding: 2">
<legend>Address Form</legend>
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse:
collapse" bordercolor="#111111" width="58%">
<tr>
<td width="24%">Address</td>
<td width="76%"><input type="text" name="address1" size="20"></td>
</tr>
<tr>
<td width="24%">Apt#</td>
<td width="76%"><input type="text" name="address2" size="20"></td>
</tr>
<tr>
<td width="24%">State</td>
<td width="76%"><select size="1" name="state" onChange="depedentDropDown(this,'ajaxOptionList.do')">
<option>Select State</option>
<option value="VA">VA</option>
<option value="MD">MD</option>
</select></td>
</tr>
<tr>
<td width="24%">City</td>
<td width="76%"><select size="1" name="city">
<option>Select City</option>
</select></td>
</tr>
<tr>
<td width="24%">Zip</td>
<td width="76%"><input type="text" name="zip" size="20"></td>
</tr>
</table>
</fieldset>
<p><input type="submit" value="Submit" name="B1"><input type="reset"
value="Reset" name="B2"></p>
</form>
</body>
</html>
</code>
Struts Action Classes:
To make the process little convenient and reusable I created an abstract class which extends Action which has an abstract method which can be implemented by extending subclasses. The super class is responsible for generating the XML and sending the request back. For turning Java Object into XML I have used Commons Betwixt an open source for Jakarta. Download the jar file and included it in the classpath. i.e., copy the betwixt.jar file into WEB-INF/lib folder of the application. (Note: If you are not using struts then betwixt has dependencies all the jar files needs to be in the classpath )
AjaxOptionsAction.java
import java.beans.IntrospectionException;
import java.io.IOException;
import java.io.StringWriter;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.betwixt.io.BeanWriter;
import org.apache.commons.betwixt.strategy.DecapitalizeNameMapper;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.xml.sax.SAXException;
public abstract class AjaxOptionsAction extends Action {
/**
- This is the main action called from the Struts framework.
- @param mapping The ActionMapping used to select this instance.
- @param form The optional ActionForm bean for this request.
- @param request The HTTP Request we are processing.
- @param response The HTTP Response we are processing.
- @return
- @throws javax.servlet.ServletException
- @throws java.io.IOException
*/
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
String buffer = getXMLObject(request);
response.addHeader("Content-Type", "text/xml");
response.setContentType("text/xml; charset=windows-1252");
response.getOutputStream().print(buffer.toString());
return null;
}
/**
- Get's the List object from the extended class and convets the List to XML
*
- @param request
- @return
*/
public String getXMLObject(HttpServletRequest request) {
List list = getDropDownList(request);
String xmlString = toXML(list, "root");
return xmlString;
}
/**
- This will convert the Object into XML
*
- @param root
- @param object
- @return
*/
public String toXML(Object object, String root) {
StringWriter outputWriter = new StringWriter();
// Betwixt just writes out the bean as a fragment
// So if we want well-formed xml, we need to add the prolog
outputWriter.write("");
outputWriter.write("\n");
// Create a BeanWriter which writes to our prepared stream
BeanWriter beanWriter = new BeanWriter(outputWriter);
beanWriter.getBindingConfiguration().setMapIDs(false);
beanWriter.getXMLIntrospector().getConfiguration().setElementNameMapper(new DecapitalizeNameMapper());
beanWriter.enablePrettyPrint();
try {
beanWriter.write(root, object);
} catch (IOException e) {
System.out.println(e.toString());
} catch (SAXException e) {
System.out.println(e.toString());
} catch (IntrospectionException e) {
System.out.println(e.toString());
}
beanWriter.setIndent(outputWriter.toString());
return new String(outputWriter.toString());
}
/**
- The class which extends this calls have to override this method and return a List Object
*
- @param request
- @return
*/
public abstract List getDropDownList(HttpServletRequest request);
}
AjaxOptionListAction.java extends AjaxOptionsAction.java and override the method getDropDownList. In the struts-config.xml the mapping would point to this class.
AjaxOptionListAction.java
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.util.LabelValueBean;
public class AjaxOptionListAction extends AjaxOptionsAction
{
public List getDropDownList(HttpServletRequest request)
{
String state = request.getParameter("state");
List list = new ArrayList();
// get the List for DB or form the session which ever is convinent
list = getList(state);
return list;
}
/*
This method is for example only
*/
public List getList(String state) {
List list = new ArrayList();
if (state.equalsIgnoreCase("VA")) {
list.add(new LabelValueBean("Reston", "Reston"));
list.add(new LabelValueBean("Chantilly", "Chantilly"));
list.add(new LabelValueBean("Centreville", "Centreville"));
list.add(new LabelValueBean("Herndon", "Herndon"));
list.add(new LabelValueBean("Eledn st", "Eledn st"));
} else if (state.equalsIgnoreCase("MD")) {
list.add(new LabelValueBean("Silver Spring", "Silver Spring"));
list.add(new LabelValueBean("Rockwill", "Rockwill"));
list.add(new LabelValueBean("BETHESDA", "BETHESDA"));
list.add(new LabelValueBean("BOYDS", "BOYDS"));
list.add(new LabelValueBean("BRANDYWINE", "BRANDYWINE"));
}
return list;
}
}
In struts-config.xml the mapping
<code>
<action path="/ajaxOptionList" type="AjaxOptionListAction"/>
</code>
That is it as simple as it is. In the next phase I will use the same example to populate the Suggest City Text Field insted of city dropdown list.
The above code works fine but if the list is too big then it takes some time to clear the list. We can modify the JavaScript in the jsp page to do much better job.
Hi Venu,
ReplyDeleteThanks for this amazing ajax example,
Venu, i used ajax cascading dropdown example using prototype.js, it's working fine on IE 7
but not on IE 6,
i tried 1.5 and 1.6 versions also.
pls suggest.
Hello Venu,
ReplyDeleteyou posted: "In the next phase I will use the same example to populate the Suggest City Text Field insted of city dropdown list".
Could I find in some post of yours the example you said above please?
Or, please, can you help me to modify the "Struts and AJAX by example - 1" to perform the ajax suggest?
Thank U Very Much!
Max