简介下一代 Web 站点的开发依赖于两个关键技术:Asynchronous JavaScript + XML(Ajax)和 JavaScript Serialized Object Notation(JSON)。业务应用程序能从这些技术中受益以提供更直观、响应性更好的用户界面。本文介绍了如何通过构建可重用的基于 Ajax 的 JavaServer Pages (JSP) TagLib 控件向 Java? Platform Enterprise Edition(Java EE)Web 应用程序中添加 Ajax 和 JSON。 在本文中,学习如何构建一个更新面板控件,该控件可以根据用户活动动态地从服务器异步检索内容。例如,可以在用户单击某个字段、在字段中输入值或从下拉列表中选择值时检索内容。本文还将描述如何构建一个弹出对话框控件,它可以在对话框显示时从服务器异步检索对话框内容。例如,可以在用户将鼠标悬停在某个项目上、单击按钮或从下拉列表中选择值时显示对话框。 关于本系列本文是由多部分组成的系列文章的其中之一,该系列文章介绍了如何为 J2EE 应用程序开发启用了 Ajax 的控件套件。这些控件均使用 JSP TagLib 控件构建并综合使用了 JSON、Servlet 和 JavaScript 技术。 关于本文本文描述如何构建以下控件:
这些 JSP TagLib 控件封装所有异步通信、JavaScript 代码、CSS 格式化和超文本标记语言 (HTML) 生成。 技术概览本文以本系列前两篇文章描述的设计原则和代码为基础(参见 参考资料)。本文将讨论一些初级和高级技巧,包括将事件处理程序添加到自定义控件,以及配置控件的外观和行为。 与本系列的上一篇文章相同,本文开发的支持 JSP Ajax 的控件的主要设计目标如下:
类似于本系列前两篇文章, 类模型本文中的示例由 Data Abstract Layer (DAL)、Data Transfer Objects (DTO)、Business Logic Layer (BLL)、Presentation Layer 和所支持的帮助类构成。本文还将使用本系列第一篇文章中的 本文将使用本系列 第一篇文章 中的 尽管您只构建两个控件,但您需要创建一些标记来确保这些控件稳定、灵活且可配置。首先来看图 1 中的更新面板控件。它是 图 1. UpdatePanelTag 的 Unified Modeling Language (UML) 类图更新面板控件支持可选参数,它们随更新面板的异步请求一同传递。 图 2. PanelArgumentTag 的 UML 类图弹出控件更加复杂,并且将使用一些标记。主要标记是 图 3. PopupDialogTag 的 UML 类图
图 4. PopupDialogButtonContainerTag 和 PopupDialogButtonTag 的 UML 类图
图 5. PopupDialogArgumentContainerTag 和 PopupDialogArgumentTag 的 UML 类图构建更新面板 JSP TagLib 控件通常,即使用户操作只造成部分页面更改(比如用户在字段中输入一个值,从下拉列表中选择值,选中复选框或单击某个控件),Web 开发人员也需要刷新整个 Web 页面。虽然内容及业务需求存在差异,但大多数业务线应用程序都包含根据用户事件而更改的动态内容。
图 6 显示了更新面板在更新时的外观。 图 6. 检索内容时的更新面板当服务器响应异步请求时,更新面板内容将被更新。图 7 显示更新面板接收并显示异步内容之后的外观。 图 7. 检索内容后的更新面板控件技术概览初次呈现页面时,将为对话框呈现一个
表 1. 更新面板的可配置选项
事件处理程序用例场景事件处理程序是此控件的一个强大特性。虽然呈现和调用钩子(hook)的代码相当直观,但它们提供了一种机制可确保控件的高度灵活性和可重用性。 您可以使用 您可以使用 您可以使用 您可以使用 构建显示或隐藏更新面板的函数
这些函数几乎相同,其作用仅仅是特定于对话框的实例的 清单 1.
|
标记 | 描述 |
---|---|
popupdialog | 弹出对话框 |
popuparguments | 可选标记,包含一个或多个传递给弹出对话框的参数(popupargument 标记) |
popupargument | 弹出对话框的弹出对话框参数 |
popupbuttoncontainer | 可选标记,包含一个或多个按钮(popupbutton 标记)。它还可以包含 HTML 用于格式化按钮布局 |
popupbutton | 弹出对话框的按钮 |
图 8 显示了弹出对话框的组件。
初次呈现页面时,将为对话框呈现一个 DIV
容器。应用样式 display: none
隐藏弹出对话框。容器将可选择性地包含一个标题面板(由 showtitle
和 title
属性决定)和一个按钮面板(由 <ajax:popupdialog/>
是否包含一个 <ajax:popupbuttoncontainer/>
标记决定)。它还将在对话框内容的对话框容器中呈现 DIV
。该元素最初为空,并且将在为相应弹出对话框调用 showDialog
时添加对话框内容。显示弹出对话框时,会发生以下事情:
display: block
,使弹出对话框可见。
updatemessage
。
另外,还对弹出对话框容器应用了两个重要属性以确保它正确运行。第一个是 z-index: 100;
,用于确保对话框容器显示在其他 Web 页面元素之上。第二个是 position: absolute;
,用于允许对弹出对话框进行定位。
弹出对话框控件提供了一些可配置的特性,用于定义弹出对话框的行为和外观。表 3 列出了受支持的可配置特性。
特性 | 描述 |
---|---|
id | 控件 ID |
标题 | 对话框标题 |
showtitle | 控制是否显示对话框标题的选项(默认:true) |
showclose | 控制是否显示关闭按钮的选项(默认:true) |
width | 对话框的宽度 |
height | 对话框的高度 |
contentheight | 可以滚动的对话框内容窗格的高度 |
left | 对话框的左定位 |
top | 对话框的上定位 |
url | 在显示对话框时检索对话框内容的 URL |
updatemessage | 检索内容时显示的消息 |
<ajax:page/>
呈现 <ajax:popupdialog/>
控件的两个关键函数:
showDialog
— 通过调用相应的 onShow
事件处理程序来显示弹出对话框。closeDialog
— 通过调用相应的 onHide
事件处理程序来隐藏弹出对话框。类似于更新面板,这些函数的作用基本相同,仅为特定于对话框的实例的 show
和 hide
和对话框函数提供包装器。在讨论更新面板时提到,这种方法抽象如何显示对话框以及如何从 <ajax:popupdialog/>
控件使用者那里异步检索内容的细节。您只需要调用 showDialog
或 closeDialog
JavaScript 函数,传递显示或隐藏更新面板的对话框的名称。
下一步是构建特定于对话框的函数来显示弹出对话框。该函数主要由 <ajax:popupdialog/>
标记呈现,包含基于在标记的属性中指定的选项的 JavaScript 代码。例如,设置对话框的标题,构建 POST 数据和 URL,以及发起异步请求。该函数如清单 7 所示。
dialogName_onShow
JavaScript 函数function stateDialog_onShow() { // Get dialog control var dialogControl = document.getElementById('stateDialog'); dialogControl.style.display = 'block'; // Set title (if showtitle attribute is not false) var titleControl = document.getElementById('stateDialog_dialogTitleText'); titleControl.innerHTML = 'Select States'; // Get dialog content control var dialogContentName = 'stateDialog_dialogContent'; var contentControl = document.stateDialog_dialogForm[dialogContentName]; // If dialog content control is not null if (contentControl != null) { contentControl.innerHTML = 'null'; } // Initialize XmlHttpRequest object initializeXmlHttpRequest(); if (req == null) { alert('XmlHttpRequest object not initialized.'); } // Url for retrieving dialog content (specified in url attribute) var targetUrl = '/ajaxcontrols3/fragments/selectState.jsp'; // Build the POST data sent for the asynchronous request to // retrieve the dialog content // The arguments specified in the optional argument tag(s) // contained in the popuparguments tag var postData = ''; // The dialogName_getPostData function is rendered by the // popuparguments tag (if specified). // Check if the function is defined and if so, invoke the // function if (window['stateDialog_getPostData'] != undefined) { postData = window['stateDialog_getPostData'](); } // Set server response function var responseFunction = 'stateDialog_onServerResponse'; req.onreadystatechange = eval(responseFunction); // Open server request req.open('POST',targetUrl,true); req.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); req.setRequestHeader("Content-length", postData.length); // Send data req.send(postData); }
下一步是处理服务器响应,这需要使用 dialogName_onServerResponse
函数。代码并不难。对话框内容被替换为异步服务器请求返回的 HTML。服务器响应函数如清单 8 所示。
dialogName_onServerResponse
JavaScript 函数function stateDialog_onServerResponse() { // If loaded if(req.readyState!=4) return; // If an error occurred if(req.status != 200) { alert('An error occurred retrieving panel content.'); return; } // Get response var responseData = req.responseText; // Get dialog content pane var dialogContentName = 'stateDialog_dialogContent'; var contentControl = document.getElementById(dialogContentName); // If dialog content pane found, set the content if (contentControl != null) { contentControl.innerHTML = responseData; } }
下一步是在 TagLib 库定义文件中定义 <ajax:popupdialog/>
JSP 控件。清单 9 显示了弹出对话框控件的 TagLib 库定义条目(为每个属性都嵌入了描述性注释)。
<tag> <name>popupdialog</name> <tagclass>com.testwebsite.controls.popupdialog.PopupDialogTag</tagclass> <bodycontent>JSP</bodycontent> <info>Popup Dialog tag.</info> <!-- Popup ID--> <attribute> <name>id</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Popup Title--> <attribute> <name>title</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Show Title --> <attribute> <name>showtitle</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Show Close Button --> <attribute> <name>showclose</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Size Width --> <attribute> <name>width</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Size Height --> <attribute> <name>height</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Size Content Height --> <attribute> <name>contentheight</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Position Left --> <attribute> <name>left</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Position Top--> <attribute> <name>top</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Url to fragment--> <attribute> <name>url</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Message displayed while retrieving panel content--> <attribute> <name>updatemessage</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> </tag>
<ajax:popupdialog/>
可以选择性地包含 <ajax:popuparguments/>
标记,发送异步请求的 POST 数据以检索对话框内容。该标记负责呈现 dialogName_getPostData
函数。清单 10 显示了一个示例 dialogName_getPostData
函数,用于返回包含 POST 数据的所有名称/值对的字符串。
dialogName_getPostData
JavaScript 函数function cityDetailsDialog_getPostData() { var data = 'dialogName=cityDetailsDialog' + '&zipCode=' + getFormValue('selectedZipCode'); return data; }
每个 popupargument
标记都呈现有各自的名称/值。以下代码片段是为各个参数呈现的:+ '&zipCode=' + getFormValue('selectedZipCode');
。稍后将详细讨论 popupargument
标记。
doStartTag
呈现 dialogName_getPostData
函数的开始。<ajax:popuparguments/>
的方法如清单 11 所示。
doStartTag
方法public int doStartTag() throws JspException { StringBuffer html = new StringBuffer(); String dialogName = this.getDialogName(); html.append("<script type='text/javascript' language='javascript'>"); html.append("function "); html.append(dialogName); html.append("_getPostData() {"); html.append("var data = 'dialogName="); html.append(dialogName); html.append("'"); JspWriter out = pageContext.getOut(); try { out.append(html.toString()); } catch (IOException e) { e.printStackTrace(); } return PopupDialogArgumentContainerTag.EVAL_BODY_INCLUDE; }
doEndTag
方法关闭 dialogName_getPostData
函数,如清单 12 所示。
doEndTag
方法public int doEndTag() throws JspException { StringBuffer html = new StringBuffer(); html.append(";"); html.append("return data;"); html.append("}"); html.append("</script>"); // Write output JspWriter out = pageContext.getOut(); try { out.append(html.toString()); } catch (IOException e) { e.printStackTrace(); } return PopupDialogArgumentContainerTag.EVAL_PAGE; }
下一步是在 TagLib 库定义文件中定义 <ajax:popuparguments/>
JSP 控件。清单 13 显示了弹出对话框参数容器的 TagLib 库定义条目(为每个属性都嵌入了描述性注释)。
<tag> <name>popuparguments</name> <tagclass>com.testwebsite.controls.popupdialog.PopupDialogArgumentContainerTag </tagclass> <bodycontent>JSP</bodycontent> <info>Popup Dialog Argument Container</info> <!-- Popup Argument Container ID--> <attribute> <name>id</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> </tag>
下一步是在 TagLib 库定义文件中定义 <ajax:popupargument/>
。弹出参数的 TagLib 库定义条目(为每个属性都嵌入了描述性注释)如清单 14 所示。
<tag> <name>popupargument</name> <tagclass>com.testwebsite.controls.popupdialog.PopupArgumentTag</tagclass> <bodycontent>empty</bodycontent> <info>Contains the argument tag.</info> <!-- Argument name --> <attribute> <name>name</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Source field for argument value --> <attribute> <name>sourcefield</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- Value for argument (overrides source field value if specified) --> <attribute> <name>value</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> </tag>
<ajax:popupdialog/>
可以选择性地包含一个按钮面板 <ajax:popupbuttoncontainer/>
标记。容器标记将呈现一个名称为 dialogName_dialogButtonContainer
的 DIV
元素。
<ajax:popupbuttoncontainer/>
标记可以包含一个或多个 <ajax:popupbutton/>
标记。弹出对话框控件支持两种按钮 —cancel
和 normal
。如果按钮类型是 cancel
,那么在单击按钮时将调用 closeDialog
。如果按钮类型是 normal
,则调用在 onclick
属性中指定的 JavaScript 函数。
按钮呈现在弹出按钮的 doStartTag
方法中。被调用的按钮和事件处理程序的类型的逻辑包含在此方法中,如清单 15 所示。
doStartTag
方法public int doStartTag() throws JspException { StringBuffer html = new StringBuffer(); // Get button container PopupDialogButtonContainerTag buttonPane = (PopupDialogButtonContainerTag) this.getParent(); String dialogName = buttonPane.getDialogName(); // Render button html.append("<input type='button' id='"); html.append(this.getId()); html.append("' class='"); html.append(this.getCssclass()); String buttonLabel = this.getLabel(); if (buttonLabel != null && buttonLabel.length() > 0) { html.append("' value='"); html.append(buttonLabel); } html.append("' onclick='"); // Check button type and render event handler accordingly String buttonType = this.getType(); if (buttonType == null || buttonType.length() < 1 || buttonType.equalsIgnoreCase("cancel")) { html.append("closeDialog(\""); html.append(dialogName); html.append("\")"); } else { html.append(this.getOnclick()); html.append("(\""); html.append(dialogName); html.append("\", document."); html.append(dialogName); html.append("_dialogForm)"); } html.append("'/>"); // Write output JspWriter out = pageContext.getOut(); try { out.append(html.toString()); } catch (IOException e) { e.printStackTrace(); } return PopupButtonTag.EVAL_BODY_BUFFERED; }
下一步是在 TagLib 库定义文件中定义 <ajax:popupbuttoncontainer/>
JSP 控件。按钮面板容器的 TagLib 库定义条目(为每个属性都嵌入了描述性注释)如清单 16 所示。
<tag> <name>popupbuttoncontainer</name> <tagclass>com.testwebsite.controls.popupdialog.PopupDialogButtonContainerTag </tagclass> <bodycontent>JSP</bodycontent> <info>Popup Dialog Button Container</info> <!-- Popup Button Container ID--> <attribute> <name>id</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> </tag>
接下来,您希望在 TagLib 库定义文件中定义 <ajax:popupbutton/>
JSP 控件。按钮的 TagLib 库定义条目(为每个属性都嵌入了描述性注释)如清单 17 所示。
<tag> <name>popupbutton</name> <tagclass>com.testwebsite.controls.popupdialog.PopupButtonTag</tagclass> <bodycontent>empty</bodycontent> <info>Contains the argument tag.</info> <!-- Button id --> <attribute> <name>id</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> <!-- Button label --> <attribute> <name>label</name> <required>false</required> <rtexprvalue>true</rtexprvalue> </attribute> <!-- Type of button --> <attribute> <name>type</name> <required>true</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- onclick Event handler --> <attribute> <name>onclick</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> <!-- CSS class name --> <attribute> <name>cssclass</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> </tag>
可以使用三个测试页面来演示新的支持 Ajax 的控件。这些页面如表 4 所示。
页面 | 描述 |
---|---|
SearchLocations.jsp | 使用 <ajax:updatepanel/> 提供对包含城市的数据单元格的实时过滤。城市列表可以按州(下拉选择列表)和城市名称(文本字段)过滤。这两个字段将在各自值发生更改时调用 showUpdatePanel 。 |
LocationReport.jsp | 使用 <ajax:popupdialog/> 提供一个模拟报表页面。报表标准包括一个州文本字段,单击该字段将显示一个弹出对话框,其中包含带复选框的所有州的列表。当用户单击 Save 按钮时,将建立一个逗号分隔的州列表,并将州文本字段的值设置到逗号分隔列表中。 |
NewYorkCityList.jsp | 显示 New York 的所有城市列表。当用户将鼠标悬停在城市列表的某个邮政编码上时,它使用 <ajax:popupdialog/> 显示城市的额外信息。鼠标移开邮政编码时将隐藏弹出对话框。 |
Search Locations 页面演示如何使用更新面板控件。图 9 显示了一个示例更新面板。
Search Locations 页面演示如何使用事件处理程序,以及如何将更新面板与两个控件挂在一起(一个 SELECT
和一个 INPUT
文本框)。清单 18 显示了此示例页面的代码。
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="ajax" uri="/WEB-INF/tlds/ajax_controls.tld"%> <html> <head> <title>Locations</title> <link href="core.css" rel="stylesheet" type="text/css" /> <ajax:page/> <script type='text/javascript' language='javascript'> function onCityPanelCleared() { var msgControl = document.getElementById('statusMessage'); if (msgControl != null) { msgControl.innerHTML = 'oncleared'; } } function onCityPanelClear() { var msgControl= document.getElementById('statusMessage'); if (msgControl != null) { msgControl.innerHTML = 'onclear'; } return true; } function onCityPanelLoad() { var msgControl = document.getElementById('statusMessage'); if (msgControl != null) { msgControl.innerHTML = 'onload'; } return true; } function onCityPanelLoaded() { var msgControl= document.getElementById('statusMessage'); if (msgControl != null) { msgControl.innerHTML = 'onloaded'; } } function displayCityPanel(curControl) { if (!curControl.checked) { hideUpdatePanel('cityPanel'); } } function retrieveCityData() { showUpdatePanel('cityPanel'); document.all.showCityPanel.checked = 'checked'; } </script> </head> <body> <b>State: </b> <select id="state" onchange="retrieveCityData()"> <option value=''> </option> <option value='AK'> Alaska</option> <option value='AL'> Alabama</option> <option value='AR'> Arkansas</option> ... ... <option value='WI'> Wisconsin</option> <option value='WV'> West Virginia</option> <option value='WY'> Wyoming</option> </select><br/> <b>City Prefix: </b> <input type="text" id="cityPrefix" onkeyup="retrieveCityData()"/> <br/> <input type="checkbox" id="showCityPanel" name="showCityPanel" checked="checked" onclick="displayCityPanel(this)" />Show City Panel<br/> <div><b>Result: </b><span id="statusMessage"></span></div> <div> <ajax:updatepanel id="cityPanel" url="/fragments/cityList.jsp" updatemessage="Retrieving cities..." cssclass="normalpanel" loadingcss="loadingpanel" onclear="onCityPanelClear" oncleared="onCityPanelCleared" onloaded="onCityPanelLoaded" onload="onCityPanelLoad"> <ajax:panelargument name="state" sourcefield="state"/> <ajax:panelargument name="cityPrefix" sourcefield="cityPrefix"/> </ajax:updatepanel> </div> </body> </html>
Location 报表演示如何使用弹出对话框允许用户选择多个州(复选框)。当用户选择弹出对话框上的 Save 按钮时,一个文本框将被设置为包含所选州的逗号分隔列表。图 10 显示了弹出对话框。
当用户双击 selectedStates
控件时,将使用 showDialog
函数显示弹出对话框。当用户单击 popupdialog
上的 Save 按钮时,将调用 onSaveStates
JavaScript 函数,这将建立一个由所选值构成的逗号分隔列表。然后,使用列表填充 selectedStates
文本框。Location 报表的代码如清单 19 所示。
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="ajax" uri="/WEB-INF/tlds/ajax_controls.tld"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Search Locations</title> <link href="core.css" rel="stylesheet" type="text/css" /> <script type="text/javascript"> function onSaveStates(dialogName, dialogForm) { var values = ''; var stateList = dialogForm.states; for (var i = 0; i < stateList.length; i++) { if (stateList[i].checked) { if (values.length != 0) { values += ", "; } values += stateList[i].value; } } var targetControl = document.getElementById('selectedStates'); targetControl.value = values; closeDialog(dialogName); } </script> <ajax:page id="page"/> </head> <body> <h1>Report Criteria</h1> <table width="80%" > <colgroup> <col width="40%"/> <col width="60%"/> </colgroup> <tbody> <tr> <td align="right" style="border:none;"> <span style="font-weight:bold">Zip Code: </span> </td> <td align="left" style="border:none;"> <input type="text" size="50" id="zipCode" /> </td> </tr> <tr> <td align="right" style="border:none;"> <span style="font-weight:bold">States: </span> </td> <td align="left" style="border:none;"> <input type="text" size="50" readonly="readonly" id="selectedStates" onclick="showDialog('stateDialog')"/> </td> </tr> </tbody> <thead> <th colspan="2" class="subHeading"> Search Locations</th> </thead> <tfoot> <tr> <td colspan="2"> <input type="button" class="dialogButton" value="Run Report" id="runReport"/> </td> </tr> </tfoot> </table> <ajax:popupdialog url="/ajaxcontrols3/fragments/selectState.jsp" id="stateDialog" width="400px" height="28em" left="0" top="0" title="Select States" contentheight="23em"> <ajax:popuparguments id="stateDialogArgs"> <ajax:popupargument name="arg1" value="test1"/> <ajax:popupargument name="arg2" value="test2"/> </ajax:popuparguments> <ajax:popupbuttoncontainer id="stateDialogButtons"> <ajax:popupbutton id="cancelButton" label="Cancel" type="cancel"/> <ajax:popupbutton id="saveButton" label="Save" type="normal" onclick="onSaveStates"/> </ajax:popupbuttoncontainer> </ajax:popupdialog> <h1>Report Results</h1> <div>Show report results here...</div> </body> </html>
清单 20 显示了在弹出对话框中显示的页面。代码相对比较简单。从 Location Data Service 检索得到一个地点列表,然后迭代此列表,并为每个州呈现一个复选框。
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ page import="com.testwebsite.dal.LocationDataService" %> <%@ page import="com.testwebsite.dto.LocationDTO" %> <%@ page import="com.testwebsite.dto.StateDTO" %> <%@ page import="java.util.Iterator" %> <%@ page import="java.util.TreeMap" %> <%@ page import="java.util.Set" %> <ul style="list-style: none;" <% TreeMap<String, StateDTO> stateData = LocationDataService.getStateData(); // Iterate through all data looking for matching // cities and add to temporary TreeMap Set<String> keySet = stateData.keySet(); Iterator<String> stateIter = keySet.iterator(); while (stateIter.hasNext()) { // Get current state String curKey = stateIter.next(); StateDTO curState = stateData.get(curKey); out.println("<li><input type='checkbox' name='states' value='"); out.println(curState.getAbbreviation()); out.println("'/> "); out.println(curState.getName()); out.println("</li>"); } %> </ul>
New York City Listing 页面演示了另一种利用弹出对话框的方法。许多 Web 应用程序中的一个常见场景就是当用户将鼠标悬停在列表中的某个项目上时显示额外信息。弹出对话框允许仅通过 Ajax 发起请求时检索详细信息。本示例如图 11 所示。
New York City Listing 页面的代码如 清单 21 所示。本示例中的关键函数是 showCityDialog
,它接受一个参数,即用户当前悬停项目的邮政编码。使用标准 CSS 属性和 JavaScript 代码设置对话框位置。然后显示 showDialog
函数。当用户从邮政编码移开时,将调用 hideDialog
函数以隐藏弹出对话框。
<html><head> <title>New York City Listing</title> <ajax:page/> <link href="core.css" rel="stylesheet" type="text/css" /> </head> <script type="text/javascript"> function showCityDialog(zipCode) { var xPosition = 0; var yPosition = 0; if (!e) var e = window.event; if (e.pageX || e.pageY) { posx = e.pageX; yPosition = e.pageY; } else if (e.clientX || e.clientY) { xPosition = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; yPosition = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; } var dialogControl = document.getElementById("cityDetailsDialog"); if (dialogControl != null) { dialogControl.style.top = yPosition; dialogControl.style.left = xPosition; } // Get selected zip code var zipCodeControl = document.getElementById("selectedZipCode"); if (zipCodeControl != null) { zipCodeControl.value = zipCode; showDialog('cityDetailsDialog'); } } </script> <body> <table cellpadding="0" cellspacing="0" width="60%"> <tbody> <% TreeMap<Integer, LocationDTO> locData = LocationDataService.getLocationData(); // Temp map to ensure only unique cities are added. HashMap tempMap = new HashMap(); // Iterate through all data looking for matching cities and add to // temporary TreeMap Set<Integer> keySet = locData.keySet(); Iterator<Integer> locIter = keySet.iterator(); while (locIter.hasNext()) { // Get current state Integer curKey = locIter.next(); LocationDTO curLocation = locData.get(curKey); String curState = curLocation.getState(); String curCity = curLocation.getCity(); String lowerCurCity = curCity.toLowerCase(); if (!tempMap.containsKey(curCity) && curState.equalsIgnoreCase("NY")) { tempMap.put(curCity, curCity); out.println("<tr>"); out.println("<td><a href='#' style='text-decoration:none;color' onMouseOver=\"showCityDialog('" + curLocation.getZipCode() + "')\" >"); out.println(curLocation.getZipCode()); out.println("</td>"); out.println("<td>"); out.println(curLocation.getCity()); out.println("</td>"); out.println("<td>"); out.println(curLocation.getCounty()); out.println("</td>"); out.println("</tr>"); } } %> </tbody> <thead> <tr> <th colspan="3"><b>Cities</b></th></tr> <tr> <th class="subHeading"> <b>Zip Code</b></th> <th class="subHeading"> <b>City</b></th> <th class="subHeading"> <b>County</b></th> </tr> </thead> </table> <input type="hidden" id="selectedZipCode" value=""/> <ajax:popupdialog url="/ajaxcontrols3/fragments/cityDetails.jsp" id="cityDetailsDialog" width="400px" left="0" top="0" title="City Information" height="10.5em" contentheight="8.5em"> <ajax:popuparguments id="stateDialogArgs"> <ajax:popupargument name="zipCode" sourcefield="selectedZipCode"/> </ajax:popuparguments> </ajax:popupdialog> </body></html>
清单 22 显示了弹出对话框内容的 JSP 代码。它相当清楚。从 Location Data Service 检索城市的详细信息,并显示相应的记录。
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ page import="com.testwebsite.dal.LocationDataService" %> <%@ page import="com.testwebsite.dto.LocationDTO" %> <%@ page import="com.testwebsite.dto.StateDTO" %> <%@ page import="java.util.Iterator" %> <%@ page import="java.util.TreeMap" %> <%@ page import="java.util.Set" %> <% TreeMap<Integer, LocationDTO> locData = LocationDataService.getLocationData(); String zipCodeStr = request.getParameter("zipCode"); Integer zipCode = new Integer(zipCodeStr); LocationDTO curLocation = locData.get(zipCode); if (curLocation != null) { out.println("<div id='cityDetails'>"); // Render zip code out.println("<div>"); out.println("<span style='font-weight:bold;color: #00628B'>Zip Code: </span>"); out.println("<span style='color: #00628B'>"); out.println(curLocation.getZipCode()); out.println("</span>"); out.println("</div>"); // Render city name out.println("<div>"); out.println("<span style='font-weight:bold;color: #00628B'>City: </span>"); out.println("<span style='color: #00628B'>"); out.println(curLocation.getCity()); out.println("</span>"); out.println("</div>"); // Render county name out.println("<div>"); out.println("<span style='font-weight:bold;color: #00628B'>County: </span>"); out.println("<span style='color: #00628B'>"); out.println(curLocation.getCounty()); out.println("</span>"); out.println("</div>"); // Render state out.println("<div>"); out.println("<span style='font-weight:bold;color: #00628B'>State: </span>"); out.println("<span style='color: #00628B'>"); out.println(curLocation.getState()); out.println("</span>"); out.println("</div>"); // Render longitude out.println("<div>"); out.println("<span style='font-weight:bold;color: #00628B'>Longitude: </span>"); out.println("<span style='color: #00628B'>"); out.println(curLocation.getLongitude()); out.println("</span>"); out.println("</div>"); // Render latitude out.println("<div>"); out.println("<span style='font-weight:bold;color: #00628B'>Latitude: </span>"); out.println("<span style='color: #00628B'>"); out.println(curLocation.getLatitude()); out.println("</span>"); out.println("</div>"); out.println("</div>"); } %>
在本文中,您学习了一些异步通信和 JSP TagLib 控件技巧,包括如何构建复合控件,如何为自定义控件添加客户端事件钩子,以及如何利用 Ajax 使 Web 页面更加动态。本文以本系列前两篇文章描述的 Ajax 通信技巧为基础。Ajax 允许您构建高度动态、用户友好并且可伸缩的 Web 应用程序。通过开发 JSP TagLib 控件,您可以缩短构建业务应用程序所需的时间。
您可以进一步扩展这些控件:
描述 | 名字 | 大小 |
---|---|---|
本文的源代码 | ArticleCodeSample.zip | 2560KB |
|