H5W3
当前位置:H5W3 > 其他技术问题 > 正文

基于AJAX和JSF打造丰富的互联网组件

 

  在本篇中,我们将向你展示怎样使用Mabon来创建一个简单而强有力的输入组件,它具有类似于Google Suggest所提供的内置的建议功能。为了使Web开发者更为容易地使用我们的JDJ InputSuggest组件,我们借助于Weblets开源工程来把外部资源,例如图标和JavaScript库,绑定到一个Java档案文件(JAR)中—由它来描述我们的JSF组件绑定。

 

  一、创建支持AJAX的JSF HtmlInputSuggest组件

 

  这个JSF AJAX输入建议方案共包括四个类,见图1。

 图1.类图:构建输入建议组件所需要的类

这些类分别是:•HtmlInputSuggest—屏幕生成器特定的子类。•HtmlRenderer—这是一个超类,它提供一些便利的方法来实现资源编码。•HtmlInputSuggestRenderer—是你的新定制的屏幕生成器,它负责把标注生成到客户端屏幕上,包括需要的资源,例如JavaScript库和式样表等。•HtmlInputSuggestTag是标签处理器。

在我们的输入建议解决方案中,我们实现了一个JavaScript库—inputSuggest.js—它包含利用Mabon从Web开发者的支持bean中检索数据的功能。在本文中,我们将详细讨论inputSuggest.js文件和HtmlInputSuggestRenderer—它们都受Mabon的影响并且提供了输入域(这些输入域都具有输入探测(type-ahead)和建议列表功能)。

二、输入建议JavaScript库

既然我们使用Mabon,因此不需要担心从支持bean中取回数据的问题。我们可以把这项任务交给Mabon来完成。然而,我们关心的是,如何处理XMLHttpRequest对象返回的数据,如何填充实际的建议列表以及如何处理用户交互。这个inputSuggest.js库中包含了大量的函数,用来处理键盘导航和鼠标交互。篇幅所限,在此我们将集中分析对该JSF HtmlInputSuggest组件有重大影响的函数。

(一)doKeyPress函数

显示于列表1中的doKeyPress函数负责处理键击事件并检查是否用户按下了TAB键。在正常情况下,这个TAB键将移出输入域并激发blur事件。对于本文中的输入建议解决方案来说,一次TAB键击也可以用于从建议列表中选择一个活动行。为此,我们需要跟踪TAB键,从建议列表中选择一行,把值添加到输入域,或者,如果没有列表数据可用的话,离开该输入域。如果发生控件导航,那么将激活doBlur()函数并关闭建议列表。

列表1—doKeyPress函数

projsf.jdj.doKeyPress = function(event){var input = (event.srcElement || event.target);var inputId = input.id;var div = document.getElementById(inputId + "$suggest");var divStyle = (div.currentStyle || div.style);if (event.keyCode == 9 && divStyle.display == "block"){div.style.display = "none";var activeRow = projsf.jdj._findActiveRow(div);input.value = activeRow.innerHTML;return false; //取消按Tab键离开输入域}return true; //继续:按Tab键离开输入域,它将调用doBlur()}

列表2—doKeyUp函数

projsf.jdj.doKeyUp = function(event){var input = (event.srcElement || event.target);var inputId = input.id;var div = document.getElementById(inputId + "$suggest");if (event.keyCode == 9)//Tab键{ return false; }else if ((div.style.display == "block" || div.childNodes.length > 0) &&(event.keyCode == 40 || event.keyCode == 38)){if (div.style.display == "none"){ div.style.display = "block"; }else {var activeRow = projsf.jdj._findActiveRow(div);switch (event.keyCode) {case 40: /向下箭头if (activeRow.nextSibling){activeRow.className = "HtmlInputSuggestRow";activeRow = activeRow.nextSibling;activeRow.className = "HtmlInputSuggestActiveRow";}break;case 38: /向上箭头if (activeRow.previousSibling){activeRow.className = "HtmlInputSuggestRow";activeRow = activeRow.previousSibling;activeRow.className = "HtmlInputSuggestActiveRow";}break;}input.value = activeRow.innerHTML;}return false;}if (event.keyCode != 8)//不是一个Backspace键{input.blur();input.focus();}if (input.value.length <= 2)div.style.display = "none";}

列表3—doChange函数  

 

projsf.jdj.doChange = function(event,doSuggestURL){var input = (event.srcElement || event.target);var inputId = input.id;var context = { _inputId: inputId };net.java.dev.mabon.send({ url: doSuggestURL,args: [input.value],callback: function(result) {projsf.jdj._callback.call(context,result);} });return true;}

列表4:_callback函数

projsf.jdj._callback = function(results){var inputId = this._inputId;var input = document.getElementById(inputId);var div = document.getElementById(inputId + "$suggest");if (results.length <= 1) {div.style.display = "none";return;}//从上下文中得到输入域IDvar input = document.getElementById(inputId);div.style.width = input.offsetWidth;while (div.firstChild) {div.removeChild(div.firstChild);}for (var i=0; i < results.length; i++) {var row = document.createElement("div");var span = document.createElement("span");var text = document.createTextNode(results[i]);row.className = "HtmlInputSuggestRow";row.appendChild(text);row.onmouseover = new Function("event", "projsf.jdj._doMouseOver(event || window.event)");row.onclick = new Function("event", "projsf.jdj._doMouseClick(event || window.event)");div.appendChild(row);}div.firstChild.className = "HtmlInputSuggestActiveRow";div.style.display = "block";window.setTimeout("projsf.jdj._selectText('" + inputId + "', " + "'" + input.value + "', " + "'" + results[0] + "')", 200);}

列表5:_selectText函数

projsf.jdj._selectText=function(inputId,initialValue,suggestion){var input = document.getElementById(inputId);if (input.value != initialValue)return;if (input.value == suggestion) return;if (input.createTextRange)//IE特定的{var selectionStart = input.value.length;input.value = suggestion;var range = input.createTextRange();range.moveStart("character", selectionStart);range.moveEnd("character", input.value.length);range.select();}else //DOM兼容的{var selectionStart = input.value.length;input.value = suggestion;input.selectionStart = selectionStart;input.selectionEnd = input.value.length;}}

列表6.HtmlInputSuggestRenderer的encodeBegin()方法

package com.apress.projsf.jdj.render.html;Import ...//(省略)/***HtmlInputSuggestRenderer用自动建议的行为生成一个传统的HtmlInputText域*.*/public class HtmlInputSuggestRenderer extends HtmlRenderer{//...public static String TITLE_ATTR = "title";public static String DO_SUGGEST_ATTR = "doSuggest";public void encodeBegin(FacesContext context,UIComponent component) throws IOException{writeScriptResource(context,"weblet://org.dojotoolkit.browserio/dojo.js");writeScriptResource(context, "weblet://net.java.dev.mabon/mabon.js");writeScriptResource(context, "weblet://com.apress.projsf.jdj/inputSuggest.js");writeStyleResource(context, "weblet://com.apress.projsf.jdj/inputSuggest.css");}

 

列表7.HtmlInputSuggestRenderer的encodeEnd()方法

 

public void encodeEnd(FacesContext context,UIComponent component) throws IOException{String valueString = _getValueAsString(context, component);String clientId = component.getClientId(context);Map attrs = component.getAttributes();String title = (String)attrs.get(TITLE_ATTR);String onchange = (String)attrs.get(ONCHANGE_ATTR);MethodBinding doSuggest = (MethodBinding)attrs.get(DO_SUGGEST_ATTR);ResponseWriter out = context.getResponseWriter();out.startElement("div", component);if (title != null)out.writeAttribute("title", title, TITLE_ATTR);// // value="[converted-value]" onchange="[onchange]" />out.startElement("input", component);out.writeAttribute("id", clientId, null);out.writeAttribute("name", clientId, null);if (valueString != null)out.writeAttribute("value", valueString, null);if (doSuggest != null){//当使用服务器端建议时,禁止浏览器自动完成功能out.writeAttribute("autocomplete", "off", null);String expression = doSuggest.getExpressionString();//从表达式中修整#{}String bindingRef = expression.substring(2, expression.length() - 1);ViewHandler handler = context.getApplication().getViewHandler();String doSuggestURL = handler.getResourceURL(context, "mabon:/" + bindingRef);out.writeAttribute("onkeypress", "return projsf.jdj.doKeyPress(event);", null);out.writeAttribute("onkeyup", "return projsf.jdj.doKeyUp(event);", null);out.writeAttribute("onchange", "projsf.jdj.doChange(event, '" + doSuggestURL + "');", null);out.writeAttribute("onblur", "return projsf.jdj.doBlur(event);", null);}out.endElement("input");out.startElement("br", null);out.endElement("br");out.startElement("div", null);out.writeAttribute("id", clientId + "$suggest", null);out.writeAttribute("class", "HtmlInputSuggest", null);out.endElement("div");}

列表8.一个使用JSF HttpInputSuggest组件的JSP页面

http://java.sun.com/JSP/Page" version="1.2"xmlns:jdj="http://projsf.apress.com/jdj" xmlns:f="http://java.sun.com/jsf/core"xmlns:h="http://java.sun.com/jsf/html" >title="Input Suggest Component" value="#{backingBean.value}"doSuggest="#{backingBean.doSuggest}" />

列表9:支持bean的value属性

package com.apress.projsf.jdj.application;import java.util.ArrayList;import java.util.List;/***BackingBean是一个inputSuggest.jspx文档的支持bean。*/public class BackingBean{public void setValue(Object value) { _value = value; }public Object getValue() { return _value; }

列表10.支持bean的doSuggest()方法

public String[] doSuggest(String initialValue) {List suggestions = new ArrayList();for (int i=0; i < _MASTER_LIST.length; i++) {if (_MASTER_LIST[i].startsWith(initialValue))suggestions.add(_MASTER_LIST[i]);}return suggestions.toArray(new String[0]);}private Object _value;static private final String[] _MASTER_LIST = new String[]{"Pro JSF and Ajax","Pro Ajax","Pro JSP 2","Pro Jakarta","Pro J2EE 1.4"};}

本文地址:H5W3 » 基于AJAX和JSF打造丰富的互联网组件

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址