本例参考了Ajax in Action书中的关于动态双组合的例子,将其完整的实现了。现一步步展示于此。
实现目标:将其实现为一个组件以便于使用。该组件功能是将第一个选择列表中选中的项目作为第二个选择列表的更新依据,根据第一个列表中的选择来动态更新第二个列表的内容。
实现手段:实现该功能有一种简单的方法就是将选择列表作为服务器控件,然后对第一个列表控件实现服务器的OnChanged事件,然后在事件里面读取数据库获取数据,并将数据填入第二个列表控件。这个方法是用服务器代码实现的,缺点是需要刷新整个页面。这里介绍一种借助ajax实现的,此方法不需刷新整个页面。
实现技术:.NET实现,服务器语言是C#,javascript技术,XMLHttpReuqest
实现步骤:
1.实现XMLHttpRequest发送请求
该方法的原理就是响应客户端控件的onchange事件,发送XMLHttpRequest请求到服务器,要求回送XML数据,客户端解析服务器响应的xml文档来更新客户端控件,因此需要用javascript来实现发送请求。
net.js文件,实现了本例需要的功能。
// JavaScript Document var net = new Object(); net.READY_STATE_UNINITIALIZED = 0; net.READY_STATE_LOADING = 1; net.READY_STATE_LOADED = 2; net.READY_STATE_INTERACTIVE = 3; net.READY_STATE_COMPLETE = 4; net.errorMsg = "unknown error!"; // 构造函数,该对象执行了所有与ajax处理相关的工作 net.ContentLoader = function (component, url, method, requestParams) { this.component = component; // 从服务器异步获取数据时调用的URL this.url = url; // http请求的方法 this.method = method; // 字符串数组形式的一些请求参数,字符串形式为key = value格式 this.requestParams = requestParams; } net.ContentLoader.prototype = { // 提供一个跨浏览器的ajax数据传输对象XMLHttpRequest getTransport: function () { var transport; if (window.XMLHttpRequest) { // 原生对象 transport = new XMLHttpRequest(); } else if (window.ActiveXObject) { // IE的ActiveX对象 try { transport = new ActiveXObject('Msxml2.XMLHTTP'); } catch (err) { transport = new ActiveXObject('Microsoft.XMLHTTP'); } } return transport; }, // 发送请求 sendRequest: function () { // 记录运行时传递的参数 var requestParams = []; for (var i = 0; i < arguments.length; i++) { requestParams.push(arguments[i]); } var request = this.getTransport(); request.open(this.method, this.url, true); request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); var oThis = this; request.onreadystatechange = function () { oThis.handleAjaxResponse(request); } request.send(this.queryString(requestParams)); }, // 将初始参数和运行时参数转换成一个字符串 queryString: function (args) { var requestParams = []; // 初始恒量参数 for (var i = 0; i < this.requestParams.length; i++) { requestParams.push(this.requestParams[i]); } // 运行时参数 for (var j = 0; j < args.length; j++) { requestParams.push(args[j]); } var queryString = ""; if (requestParams && requestParams.length > 0) { for (var i = 0; i < requestParams.length; i++) { queryString += requestParams[i] + '&'; } queryString = queryString.substring(0, queryString.length - 1); } return queryString; }, // 响应处理 handleAjaxResponse: function (request) { if (request.readyState == net.READY_STATE_COMPLETE) { if (this.isSuccess(request)) { // 响应的消息组件 this.component.ajaxUpdate(request); } else { // 出错的消息组件 this.component.handleError(net.errorMsg); } } }, isSuccess: function (request) { return request.status == 0 || (request.status >= 200 && request.status < 300); } };
2.双组合列表组件处理脚本
该脚本实现了列表组件的第一个列表的onchange事件和服务器响应处理等内容。
DoubleCombo.js文件如下:
// JavaScript Document // 双组合组件构造函数 function DoubleCombo(masterId, slaveId, url, options) { this.master = document.getElementById(masterId); this.slave = document.getElementById(slaveId); this.options = options; this.ajaxHelper = new net.ContentLoader(this, url, "POST", options.requestParameters || []); this.initializeBehavior(); } DoubleCombo.prototype = { // 此方法在主select元素上设置了一个onchange事件处理函数 initializeBehavior: function () { var oThis = this; this.master.onchange = function () { oThis.masterComboChanged(); } } , // 创建请求参数并发送ajax请求,接收到的响应将定向到ajaxUpdate方法 masterComboChanged: function () { var query = this.master.options[this.master.selectedIndex].text; this.ajaxHelper.sendRequest('q=' + query); } , // 响应处理 ajaxUpdate: function (request) { // 从响应的XML中获取select元素的option var slaveOptions; try { slaveOptions = this.createOptions(request.responseXML.documentElement); } catch (e) { net.errorMsg = "Fail to create the slave options!"; } this.slave.length = 1; for (var i = 0; i < slaveOptions.length; i++) { try { this.slave.add(slaveOptions[i], null); } catch (e) { this.slave.add(slaveOptions[i]); } } } , // 完成从XML响应文档中获取值的工作,并用获取的值创建options对象 createOptions: function (ajaxResponse) { var newOptions = []; if (ajaxResponse == null) { net.errorMsg = "Can not receive the XML data!"; } var entries = ajaxResponse.getElementsByTagName('entry'); for (var i = 0; i < entries.length; i++) { var text = this.getElementContent(entries[i], 'optionText'); var value = this.getElementContent(entries[i], 'optionValue'); newOptions.push(new Option(text, value)); } return newOptions; } , getElementContent: function (element, tagName) { var childElement = element.getElementsByTagName(tagName)[0]; return (childElement.text != undefined) ? childElement.text : childElement.textContent; } , // 错误处理延迟给了使用组件的应用,应该在应用了该组件的文档中定义 handleError: function (errorMsg) { if (this.options.errorHandler) { this.options.errorHandler(errorMsg); } } };
3.服务器端实现
服务器代码处理客户端请求和发送响应文档。由于是代码隐藏技术,因此前台文件为ResponseXML.aspx
后台为ResponseXML.aspx.cs。
前台aspx文件只能包含以下部分,不能有html标签,否则发送的XML文件无效。
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="ResponseXML.aspx.cs" Inherits="DoubleComboXML"%>
后台代码
using System; using System.Data; using System.Web; using System.Web.Security; using System.Xml; using System.Text; using System.IO; using System.Data.SqlClient; public partial class DoubleComboXML : System.Web.UI.Page { #region page_load_init_code protected void Page_Load(object sender, EventArgs e) { Response.ContentType = "text/xml"; string strQuery = Request.Form["q"]; string strElem = Request.Form["master"]; string strForm = Request.Form["form"]; string cmdString = ""; if (strElem == "region") { cmdString = "SELECT TerritoryName AS optionText,TerritoryID AS optionID FROM [user].tb_Territory WHERE RegionID=(SELECT RegionID FROM [user].tb_Region WHERE RegionName=" + "'" + strQuery + "'" + ")"; } else if (strElem == "territory") { cmdString = "SELECT SubTerritoryName AS optionText,SubTerritoryID AS optionID FROM [user].tb_SubTerritory WHERE TerritoryID=(SELECT TerritoryID FROM [user].tb_Territory WHERE TerritoryName=" + "'" + strQuery + "'" + ")"; } SqlConnection myCon = new SqlConnection("Data Source=LIWEN-C1733EFC3;Initial Catalog=test;User ID=test_user;Password=585211"); SqlCommand mySqlCmd = new SqlCommand(cmdString, myCon); SqlDataAdapter myDA = new SqlDataAdapter(mySqlCmd); DataTable myDT = new DataTable(); try { myCon.Open(); myDA.Fill(myDT); } finally { myCon.Close(); } StringBuilder strXML = new StringBuilder("<?xml version=\"1.0\" ?>"); strXML.Append("<selectChoice>"); strXML.Append("<selectElement>"); strXML.Append("<formName>" + strForm + "</formName>"); strXML.Append("<formElem>" + strElem + "</formElem>"); strXML.Append("</selectElement>"); if (myDT.Rows.Count > 0) { foreach (DataRow myRow in myDT.Rows) { strXML.Append("<entry>"); strXML.Append("<optionText>" + myRow["optionText"] + "</optionText>"); strXML.Append("<optionValue>" + myRow["optionID"] + "</optionValue>"); strXML.Append("</entry>"); } } strXML.Append("</selectChoice>"); Response.Write(strXML.ToString()); } #endregion }
4.在网页中应用
新建一个aspx网页测试该组件,该网页创建了2个双组合列表,这样就形成了三组合列表,第二个列表选项取决于第一个,而第三个的取决于第二个。并且可以继续扩展下去,只需将服务器代码中的读取数据的代码修改即可。
<%@ Page Language="C#"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head> <title></title> <script type="text/javascript" src="net.js"></script> <script type="text/javascript" src="DoubleCombo.js"></script> <script type="text/javascript"> function injectComponentBehaviors() { //设置发送到服务器的参数并将错误处理绑定到该网页中的定义的myErrorHandler函数 var Options1 = { requestParameters: ["master=region", "form=Form1"], errorHandler: myErrorHandler } ; var Options2 = { requestParameters: ["master=territory", "form=Form1"], errorHandler: myErrorHandler } ; new DoubleCombo('region', 'territory', 'ResponseXML.aspx', Options1); new DoubleCombo('territory', 'subTerritory', 'ResponseXML.aspx', Options2); } function myErrorHandler(errorMsg) { alert(errorMsg); } </script> </head> <body onload="injectComponentBehaviors()"> <form id="Form1" runat="server"> <select id="region" name="region"> <option value="-1">请选择地区</option> <option value="1">华东</option> <option value="2">华南</option> <option value="3">华北</option> <option value="4">华西</option> <option value="5">华中</option> </select> <select id="territory" name="territory" style="width:200px"> <option value="-1">请选择省市</option> </select> <select id="subTerritory" name="subTerritory" style="width:200px"> <option value="-1">请选择城市</option> </select> </form> </body> </html>
效果预览
未选择第一项前,第二项空
选择了第一个列表后,第二个列表填充了相应的数据
选择第二个后,第三个也填充了数据