Populating one select menu based on another select menu using AJAX in Struts2
I'm trying to use AJAX at very first time in Struts2. Therefore, I do not have precise idea about it.
There are two <s:select>
s, one holds a list of countries which is loaded on page load and another holds a list of states that corresponds to the country selected from the country menu.
Therefore, the state menu should be initialized on the onchange
JavaScript event triggered by the country menu.
The incomplete version of such a JavaScript function is as follows.
var timeout;
var request;
function getStates(countryId)
{
if(!request)
{
if(countryId===""||countryId===null||countryId===undefined||isNaN(countryId))
{
$('#stateList').html("Write an empty <select>");
alert("Please select an appropriate option.");
return;
}
request = $.ajax({
datatype:"json",
type: "GET",
contentType: "application/json",
url: "PopulateStateList.action?countryId="+countryId,
success: function(response)
{
if(typeof response==='object'&&response instanceof Array) //Array or something else.
{
$('#stateList').html(writeResponseSomeWay);
$('#temp').remove();
}
},
complete: function()
{
timeout = request = null;
},
error: function(request, status, error)
{
if(status!=="timeout"&&status!=="abort")
{
alert(status+" : "+error);
}
}
});
timeout = setTimeout(function() {
if(request)
{
request.abort();
alert("The request has been timed out.");
}
}, 300000); //5 minutes
}
}
<s:select>
for country:
<s:select id="country"
onchange="getStates(this.value);"
name="entity.state.countryId"
list="countries" value="entity.state.country.countryId"
listKey="countryId" listValue="countryName"
headerKey="" headerValue="Select"
listTitle="countryName"/>
<s:select>
for state:
<s:select id="state"
name="entity.stateId"
list="stateTables" value="entity.state.stateId"
listKey="stateId" listValue="stateName"
headerKey="" headerValue="Select"
listTitle="stateName"/>
The above JavaScript/jQuery function sends an AJAX request to PopulateStateList.action
which is mapped to a method in an action class as follows.
List<StateTable>stateTables=new ArrayList<StateTable>(); //Getter only.
@Action(value = "PopulateStateList",
results = {
@Result(name=ActionSupport.SUCCESS, location="City.jsp"),
@Result(name = ActionSupport.INPUT, location = "City.jsp")},
interceptorRefs={@InterceptorRef(value="modelParamsPrepareParamsStack", params={"params.acceptParamNames", "countryId", "validation.validateAnnotatedMethodOnly", "true", "validation.excludeMethods", "getStateList"})})
public String getStateList() throws Exception
{
System.out.println("countryId = "+countryId);
stateTables=springService.findStatesByCountryId(countryId);
return ActionSupport.SUCCESS;
}
This method is invoked, when a country is selected in its menu and the countryId
supplied as a query-string parameter is retrieved but how to return/map this list, stateTables
to initialize/populate <s:select>
of state menu?
I have earlier used JSON with Jackson and Gson in Spring MVC. Using Gson, for example, a JSON response could simply be mapped like as follows (using a Servlet for the sake of simplicity).
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(name = "AjaxMapServlet", urlPatterns = {"/AjaxMapServlet"})
public class AjaxMapServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
Map<String, String>map=new HashMap<String, String>();
map.put("1", "India");
map.put("2", "America");
map.put("3", "England");
map.put("4", "Japan");
map.put("5", "Germany");
Type type=new TypeToken<Map<String, String>>(){}.getType();
Gson gson=new Gson();
String jsonString=gson.toJson(map, type);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(jsonString);
}
}
and in JSP, this Map
could be written as follows.
$('#btnMap').click(function() {
$.get('/TestMVC/AjaxMapServlet', function(responseJson) {
var $select = $('#mapSelect');
$select.find('option').remove();
$('<option>').val("-1").text("Select").appendTo($select)
$.each(responseJson, function(key, value) {
$('<option>').val(key).text(value).appendTo($select);
});
});
})
<input type="button" id="btnMap" name="btnMap" value="Map"/><br/>
<select id="mapSelect" name="mapSelect"><option>Select</option></select>
<select>
will be populated on pressing the given button, btnMap
.
What about Struts2? How to populate an <s:select>
based on another <s:select>
?
Solution 1:
The same way you can do it in Struts2, but instead of servlet you can use the action. For example
@Action(value="/PopulateStateList", results=@Result(type="json", params = {"contentType", "application/json", "root", "map"}))
public class AjaxMapAction extends ActionSupport {
Long countryId; //getter and setter
Map<String, String> map=new HashMap<String, String>();
public Map<String, String> getMap() {
return map;
}
@Override
public String execute() throws Exception {
map.put("1", "India");
map.put("2", "America");
map.put("3", "England");
map.put("4", "Japan");
map.put("5", "Germany");
return SUCCESS;
}
}
Now, you can use the JSON on the client
success: function(response)
{
if(typeof response==='object')
{
var $select = $('#state');
$select.find('option').remove();
$('<option>').val("-1").text("Select").appendTo($select)
$.each(response, function(key, value) {
$('<option>').val(key).text(value).appendTo($select);
}
},