• No results found

Dynamically Loading List Boxes

In document Foundations Of Ajax (2006) pdf (Page 106-113)

Web applications are often built using the “wizard” design philosophy, where each screen asks the user for small bits of input and each successive page’s data is built from the previous screen’s input. This design pattern is quite useful for certain types of scenarios such as when the user is performing a task that is best completed in a step-by-step, ordered manner. Unfortunately, too many Web applications use this approach because they have to do so. Before Ajax techniques were available, it was difficult if not impossible to update a page dynamically without refreshing the entire page when certain parts of the page needed to change based on user input.

Figure 4-4.Reading a single response header, in this case Last-Modified

One technique to avoid constant page refreshes is to hide data on the page and display it as needed. In the situation where the values in select box B are populated depending on the selected value in select box A, the available values for select box B can be held in hidden select boxes. When the selected value in select box A changes, JavaScript can determine which hidden select should be displayed. That select box can then be made visible and the previous select box hidden. Another variation is to dynamically populate the optionelements of select box B with the elements from a hidden list box. These techniques are useful, but their use is limited to sce- narios where the changes to the page are restricted to a finite set of choices based on the user’s input, and even then the set choices must be relatively small.

Say you’re building an online classified ads service for automobiles. Potential buyers search for their desired autos by specifying a car’s model year, make, and model. To avoid typographical errors on a user’s part and reduce the number of dynamic validations required, you’ve decided that the model year, make, and model input fields should all be select boxes and that the ads should go back 25 model years. Changing the selection in the model year

selectbox or the manufacturer select box must change the list of available models for that model year and manufacturer.

Remember that with each model year the number of available makes changes as new nameplates are introduced and old ones are retired. Also keep in mind that the models avail- able from each manufacturer vary from year to year. With dozens of manufacturers and several models available per manufacturer per model year, the number of combinations of model year, make, and model is staggering. That is far too many combinations to populate the select boxes using JavaScript alone.

You can solve this problem easily using Ajax techniques. Each time the selection in the model year or manufacturer select boxes changes, an asynchronous request is sent to the server requesting the list of models available for that particular manufacturer in that particular model year. The server is responsible for determining the list of models for the make and model year requested by the browser. The server will most likely employ a high-speed data lookup compo- nent, possibly implemented as a relational database, to perform the actual work of finding the available models. Once the available models are found, the server packages them in an XML file and returns them to the browser.

The browser is responsible for parsing the server’s XML response and populating the model select box with the available models for the specified make and model year. In this case, notice how well the view of the data is separated from the raw data. The browser is solely responsible for rendering the view of the data. The server is responsible for mining the raw data that must be rendered into a view on the browser.

Listing 4-5 demonstrates how you can use Ajax techniques to dynamically create the con- tents of one select box based on the values of two other list boxes. The example’s use case is the classified ads scenario described, where the selected values in a model year select box and a manufacturer select box determine the contents of the model select box. This example uses only four model years, three manufacturers, and four models available from a certain manu- facturer for a certain model year. Still, this makes 48 combinations of model year, make, and model! This example would be impossible to do by hiding the list of models for every model year and manufacturer combination and showing the appropriate list depending on the selected manufacturer and model year values.

Listing 4-5.dynamicLists.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml">

<head>

<title>Dynamically Filling Lists</title>

<script type="text/javascript"> var xmlHttp; function createXMLHttpRequest() { if (window.ActiveXObject) { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } else if (window.XMLHttpRequest) { xmlHttp = new XMLHttpRequest(); } } function refreshModelList() {

var make = document.getElementById("make").value;

var modelYear = document.getElementById("modelYear").value;

if(make == "" || modelYear == "") { clearModelsList();

return; }

var url = "RefreshModelList?"

+ createQueryString(make, modelYear) + "&ts=" + new Date().getTime();

createXMLHttpRequest();

xmlHttp.onreadystatechange = handleStateChange; xmlHttp.open("GET", url, true);

xmlHttp.send(null); }

function createQueryString(make, modelYear) {

var queryString = "make=" + make + "&modelYear=" + modelYear; return queryString;

function handleStateChange() { if(xmlHttp.readyState == 4) { if(xmlHttp.status == 200) { updateModelsList(); } } } function updateModelsList() { clearModelsList();

var models = document.getElementById("models");

var results = xmlHttp.responseXML.getElementsByTagName("model"); var option = null;

for(var i = 0; i < results.length; i++) { option = document.createElement("option"); option.appendChild (document.createTextNode(results[i].firstChild.nodeValue)); models.appendChild(option); } } function clearModelsList() {

var models = document.getElementById("models"); while(models.childNodes.length > 0) { models.removeChild(models.childNodes[0]); } } </script> </head> <body>

<h1>Select Model Year and Make</h1>

<form action="#">

<span style="font-weight:bold;">Model Year:</span> <select id="modelYear" onchange="refreshModelList();">

<option value="">Select One</option> <option value="2006">2006</option> <option value="1995">1995</option> <option value="1985">1985</option> <option value="1970">1970</option> </select>

<br/><br/>

<span style="font-weight:bold;">Make:</span> <select id="make" onchange="refreshModelList();">

<option value="">Select One</option>

<option value="Chevrolet">Chevrolet</option> <option value="Dodge">Dodge</option> <option value="Pontiac">Pontiac</option> </select> <br/><br/> <span style="font-weight:bold;">Models:</span> <br/>

<select id="models" size="6" style="width:300px;">

</select>

</form>

</body> </html>

The update of the page is driven by the onchangeevent of the make and model year select boxes. Whenever the selected value in either of the select boxes changes, the browser sends an asynchronous request to the server. The request is sent with a query string containing the val- ues of the selected make and model year.

The RefreshModelListservlet accepts the request from the browser and determines the list of models for the specified make and model year. The servlet first parses the query string to determine the requested make and model year. Once the requested make and model year have been determined, the servlet iterates over a collection of objects representing the avail- able model year, manufacturer, and model combinations. If a particular object’s model year and manufacturer properties match the requested model year and manufacturer, then the object’s model property is added to the response XML string. Once all the models for the spec- ified make and model year have been found, the response XML is written back to the browser.

Please note that in a real-world implementation the server-side piece would almost cer- tainly not rely on hard-coded values to populate the select box. The most likely implementation would involve searching a high-speed database for the models available for the requested model year and manufacturer.

Listing 4-6 shows RefreshModelListServlet.java.

Listing 4-6.RefreshModelListServlet.java package ajaxbook.chap4; import java.io.*; import java.util.ArrayList; import java.util.Iterator; import java.util.List;

import javax.servlet.*; import javax.servlet.http.*;

public class RefreshModelListServlet extends HttpServlet {

private static List availableModels = new ArrayList();

protected void processRequest(HttpServletRequest request

, HttpServletResponse response) throws ServletException, IOException {

response.setContentType("text/html;charset=UTF-8");

int modelYear = Integer.parseInt(request.getParameter("modelYear")); String make = request.getParameter("make");

StringBuffer results = new StringBuffer("<models>"); MakeModelYear availableModel = null;

for(Iterator it = availableModels.iterator(); it.hasNext();) { availableModel = (MakeModelYear)it.next(); if(availableModel.modelYear == modelYear) { if(availableModel.make.equals(make)) { results.append("<model>"); results.append(availableModel.model); results.append("</model>"); } } } results.append("</models>"); response.setContentType("text/xml"); response.getWriter().write(results.toString()); }

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

processRequest(request, response); }

public void init() throws ServletException {

availableModels.add(new MakeModelYear(2006, "Dodge", "Charger")); availableModels.add(new MakeModelYear(2006, "Dodge", "Magnum")); availableModels.add(new MakeModelYear(2006, "Dodge", "Ram")); availableModels.add(new MakeModelYear(2006, "Dodge", "Viper")); availableModels.add(new MakeModelYear(1995, "Dodge", "Avenger")); availableModels.add(new MakeModelYear(1995, "Dodge", "Intrepid")); availableModels.add(new MakeModelYear(1995, "Dodge", "Neon")); availableModels.add(new MakeModelYear(1995, "Dodge", "Spirit"));

availableModels.add(new MakeModelYear(1985, "Dodge", "Aries")); availableModels.add(new MakeModelYear(1985, "Dodge", "Daytona")); availableModels.add(new MakeModelYear(1985, "Dodge", "Diplomat")); availableModels.add(new MakeModelYear(1985, "Dodge", "Omni")); availableModels.add(new MakeModelYear(1970, "Dodge", "Challenger")); availableModels.add(new MakeModelYear(1970, "Dodge", "Charger")); availableModels.add(new MakeModelYear(1970, "Dodge", "Coronet")); availableModels.add(new MakeModelYear(1970, "Dodge", "Dart"));

availableModels.add(new MakeModelYear(2006, "Chevrolet", "Colorado")); availableModels.add(new MakeModelYear(2006, "Chevrolet", "Corvette")); availableModels.add(new MakeModelYear(2006, "Chevrolet", "Equinox")); availableModels.add(new MakeModelYear(2006, "Chevrolet", "Monte Carlo")); availableModels.add(new MakeModelYear(1995, "Chevrolet", "Beretta")); availableModels.add(new MakeModelYear(1995, "Chevrolet", "Camaro")); availableModels.add(new MakeModelYear(1995, "Chevrolet", "Cavalier")); availableModels.add(new MakeModelYear(1995, "Chevrolet", "Lumina")); availableModels.add(new MakeModelYear(1985, "Chevrolet", "Cavalier")); availableModels.add(new MakeModelYear(1985, "Chevrolet", "Chevette")); availableModels.add(new MakeModelYear(1985, "Chevrolet", "Celebrity")); availableModels.add(new MakeModelYear(1985, "Chevrolet", "Citation II")); availableModels.add(new MakeModelYear(1970, "Chevrolet", "Bel Air")); availableModels.add(new MakeModelYear(1970, "Chevrolet", "Caprice")); availableModels.add(new MakeModelYear(1970, "Chevrolet", "Chevelle")); availableModels.add(new MakeModelYear(1970, "Chevrolet", "Monte Carlo"));

availableModels.add(new MakeModelYear(2006, "Pontiac", "G6"));

availableModels.add(new MakeModelYear(2006, "Pontiac", "Grand Prix")); availableModels.add(new MakeModelYear(2006, "Pontiac", "Solstice")); availableModels.add(new MakeModelYear(2006, "Pontiac", "Vibe")); availableModels.add(new MakeModelYear(1995, "Pontiac", "Bonneville")); availableModels.add(new MakeModelYear(1995, "Pontiac", "Grand Am")); availableModels.add(new MakeModelYear(1995, "Pontiac", "Grand Prix")); availableModels.add(new MakeModelYear(1995, "Pontiac", "Firebird")); availableModels.add(new MakeModelYear(1985, "Pontiac", "6000")); availableModels.add(new MakeModelYear(1985, "Pontiac", "Fiero")); availableModels.add(new MakeModelYear(1985, "Pontiac", "Grand Prix")); availableModels.add(new MakeModelYear(1985, "Pontiac", "Parisienne")); availableModels.add(new MakeModelYear(1970, "Pontiac", "Catalina")); availableModels.add(new MakeModelYear(1970, "Pontiac", "GTO")); availableModels.add(new MakeModelYear(1970, "Pontiac", "LeMans")); availableModels.add(new MakeModelYear(1970, "Pontiac", "Tempest")); }

private static class MakeModelYear { private int modelYear;

private String make; private String model;

public MakeModelYear(int modelYear, String make, String model) { this.modelYear = modelYear; this.make = make; this.model = model; } } }

Figure 4-6 shows that selecting a different value in either select box updates the model list.

In document Foundations Of Ajax (2006) pdf (Page 106-113)