内容


Rational Application Developer V7.0 中 Typeahead 控件的高级用法

增加服务器端的动态过滤、样式、使用事件 API 及更多功能!

Comments

序言

IBM® Rational® Application Developer 中的 Enhanced JavaServer Faces™ (JSF™) Components 和 Standard JSF Components,提供了能够迅速开发丰富的、健壮的 Web 应用的高级功能。不但扩展了资源库,而且每个组件自身都是通用的、可重复使用的、(也许最重要的是)可自定义的。这种灵活性涉及到客户端的组件结构,和服务器端的业务逻辑。Enhanced Faces Typeahead 组件是一个很好的例子。本文将使用大量高级技术以讲述如何方便的定制与获得大部分 Typeahead 组件的输出。主要包括:

  • 创建服务器端动态过滤器
  • 使用 CSS 和标记属性改变外观
  • 使用 Typeahead 事件和 JavaScript™ API

本文假设您已经熟悉了 Rational Application Developer 中的 JSF 组件工具。为了循序渐进地了解如何创建您的第一个 Typeahead 组件,请参阅资源 部分中的介绍性的文章。

实现一个服务器端的动态过滤器

Typeahead 组件最直接的使用案例包括一个简单的字典查询。例如,当您在具有下拉显示最流行搜索条的搜索框中输入字符时,最开始显示的都是用户输入过的字符(如图 1所示)。

图 1. 简单的 Typeahead
typeahead with the word zygobranch
typeahead with the word zygobranch

后台中发生的是:

  1. 在客户端,脚本(JavaScript编程语言)显示了已在输入栏中输入字符(发生了 keydown 事件)。
  2. Enhanced Faces JavaScript Library backchannel 对象在后台将搜索词提交到服务器端。backchannel 封装了所有的功能和逻辑,包括了发送 XMLHttpRequest 到服务器端,或是通过通过隐藏 iFrame 提交信息。
  3. 在服务器端,响应是以 XML 的形式存在的。您能够在 JavaScript 浏览器控制台追踪 XMLHttpRequest 路径看到响应(和需求)。一个典型的 XML 响应(两种XML岛)的代码如列表1所示:
    列表 1. 带有两条结果的 Typeahead XML 服务器端响应
    <?xml version='1.0' encoding='ISO-8859-1'?>
    <taresponse>
    	<suggestlist>
    		<suggestion>bd0772-Ja</suggestion>
    		<suggestion>ede651-Ja</suggestion>
    	</suggestlist>
    </taresponse>
    <url>/test2/myPage.faces?$$ajaxid=form1:typeahead1&$$ajaxmode=axpartial</url>
  4. 脚本中, Callback 函数提供了 XMLHttpRequest。它作为响应中的参数被传递和调用。之后 Typeahead 脚本通过插入 HTML tables 和 divs 系统在 Document Object Model (DOM)中创建了推荐列表。

从用户的角度来看,下拉栏的应用是无缝地,且不需要重载页面。一旦弹出列表,您可以用您的鼠标或键盘从列表中选择选项。就像其他的标准下拉控件一样,实现了输入功能。

您常常需要引入更复杂的逻辑以管理后端搜索。以下的例子显示了过滤器如何应用于搜索过程中。

Bean 结构

本例中,结构如图 2所示。

图 2. 简单 bean 的结构
filter and search logic, and data structure
filter and search logic, and data structure

bean 包(typeaheadFilter.zip,可在 下载部分下载)结构被一分为二。bean 的数据结构在一边:

  • Company
  • Customer
  • Order

正如您所期望的,是您熟悉的模式:拥有许多客户的公司,握有大量订单。

另一边是搜索逻辑。绑定到 Typeahead 的 Map 是 SimpleSearch bean,它参照了 SampleFilter bean。过滤 bean 包括有关当前过滤器的状态信息。搜索 bean 能够获取过滤器 bean 的状态以决定在推荐列表中返回哪些结果。两边都需要被 Root bean 引用。

Root bean 创建了一个 Company 对象。Company 对象返回了 Customer 类型的四个对象:

  • Wender's Washers
  • Jackson's Juggernauts
  • Pierre's Plumbing
  • William's Web Development

每个 Customer 产生了30个 Order 类的对象。 对于每个 Order,ID String 都包含了一个16进制值(等于 0xf00000),后跟连字号,后跟客户名字的前两个字母(例如,ede651-Ja 或 bd0772-Ja)。虽然这些数字不保证唯一,但对于演示的目的已经足够了。这些是 Typeahead 会搜索的代码。

编写代码逻辑

扩展了 java.util.AbstractMap 类(本例中,如列表 2所示,SimpleSearch bean)的 bean 封装了 Typeahead 的搜索逻辑。 Typeahead 调用 Map bean 的 get 方法。 get 方法会返回下列之一:

  • 包含了 Strings 的 ArrayList containing
  • 简单的 String (在单个结果的情况下返回)
  • null
	public Object get(Object key) {
		Customer[] customers = filter.root.company.getCustomers();
		ArrayList matches = new ArrayList();
		int len=0;
		String s = (String)key;
		System.out.println("in search, key: " + key);
		for(int i=0; i<customers.length; i++)
		{
			// the following line filters out customers which don't match our filter
			if (!customers[i].CustomerName.equalsIgnoreCase(filter.getFilter())) continue;
			System.out.println("using customer: " + customers[i].CustomerName);
			// if it gets to here we have a 
			Order[] orders = customers[i].getOrders();
			for (int j=0; j<orders.length; j++){
				String record = (String) orders[j].getOrderID();
				if (record.length() > s.length()) {
					String subrecord = record.substring(0,s.length());
					if (subrecord.equalsIgnoreCase(s)==true) {
						System.out.println("adding record to matches: " + record);
						matches.add(len++,record);
					}
				}
			}
		}
		return matches;
	}

列表 2中,注意两条粗线,它改变了所有搜索顺序,唯一搜索的是 Customer Name 匹配过滤器的字符。因此,如果将过滤器设为 "Pierre's Plumbing",就会仅仅搜索相关联的客户订单。这是一个很简单的脚本,但是您可以应用相同的技术,例如:

  • 在一定范围的日期中获取所有客户订单
  • 基于环境变量的过滤

在 Rational Application Developer 中创建简单的验证应用

您可以在 Rational Application Developer 中创建显示以上过滤行为的应用。为创建一个新的应用并且导入 beans,执行如下步骤。

  1. 创建一个新的支持 Faces 的 Dynamic Web Project ,目标是您的开发环境中的服务器,如图 3 所示。 Typeahead 支持的服务器是 IBM® WebSphere® Application Server (V6.x or later), IBM® WebSphere® Portal Server (V5.1 or later),和 Apache Tomcat V5.5。
    图 3. New Dynamic Web Project 对话框
    enter project name, contents, and target runtime
    enter project name, contents, and target runtime
  2. 为项目导入 beans。右击项目,并选择 Import > Archive File,然后进入储存 typeaheadFilter.zip 文件的文件夹。确保选择将文件导入项目中的源文件夹(默认是"src"),如图 4所示。
    图 4. 导入 JavaBean 包
    import from archive file to folder
    import from archive file to folder

    下一步,您需要创建 JSP™(JavaServer Pages™)页面,然后创建页面数据。
  3. 右击文件夹并且选择 New > Web Page,在 WebContent 文件夹中创建一个新的 Web 页面,如图 5所示。
    图 5. 创建新的 JSP
    do so in the Project Explorer
    do so in the Project Explorer

    为页面输入名称并且选择 Finish
  4. 在 Page Data 视图中,创建一个新的 Faces Managed Bean。在 Add JavaBean 对话框中的 Class 部分,输入 com.ibm.devworks.typeaheadfilter.Root,如图 6所示。您可以使用右边的 helper 按钮快速实现。确保将 Scope设置为 session
    图 6. 添加一个 Faces Managed Bean, "Root"
    make the bean reusable
    make the bean reusable

    现在,您需要为数据输入和输出增加一些组件。
  5. 从 Page Data 视图将过滤类的 filter 拖至页面,如图 7所示。从对话框中选择 Inputting data
    图 7. 为 Filter 添加控制
    green arrow shows where to drag
    green arrow shows where to drag
  6. 从 Enhanced Faces Components 中(位于右面)将输入栏拖入页面中。(如果框架不存在,您需要从 Palette Customization 对话框显示它。)这一输入栏将成为您的 Typeahead 控件。本例(图 8)有一张表格和文本以识别栏并整齐的布置。
  7. 虽然这不是必需的,但是您可以从 Page Data 视图将 Company 结点拖至页面,用以显示网状 DataTable 中的数据内容。这可以在开发阶段帮助可视化数据。
    图 8. Design 视图中使用了控件的 JSP
    index.jsp tab
    index.jsp tab

    这种情况下您需要用 Typeahead 实现输入栏。
  8. 在输入栏的 Behavior 标签( Properties 视图中),选择 Enable typeahead option 检验栏实现 Typeahead 功能。注意在 Source 视图中您可以看到 hx:inputHelperTypeahead 标签已被加入 h:inputText 子标签。
  9. Design 标签,从 Page Data 视图将搜索对象拖至紧邻输入栏的 Typeahead 帮助按钮,如图 9 所示。(注意:如果您需要提供包括 Map 的类型,可以使用 java.lang.Object)。
    图 9. 拖拽搜索对象以绑定 Typeahead
    again shown with a green arrow
    again shown with a green arrow

现在可以在服务器上运行程序了,运行时如与 图 9类似。输入不同的客户名称并点击 Submit,您会发现(图 10) Typeahead 的结果会根据过滤器当前设置的 Customer 改变。您可以立即应用。

如果您投入更多的时间,您可以开发出更复杂的过滤器。逻辑上的下一步也是实现 Filter (Customer 输入) 。这对于具有大量客户十分有用。

图 10. 运行于服务器上 (注意仅仅返回 Wender 的 Orders )
typeahead results filtered by customer
typeahead results filtered by customer

本例使用 JavaBeans 技术,因为它十分简单。但是,您可以使用任意终端作为数据存储。这非常有用。例如,您可以为任务裁减 SQL 查询,并且使用 JDBC (Java™ Database Connectivity API) 连接数据库。

事件处理

Typeahead 的事件是 inProgressoncompleteonstartonerror。 事件处理是 Typeahead 的 Quick Edit 视图所指定的。您可以利用 JavaScript 将它们连在一起。这时,关注于为 oncomplete事件增加一个 JavaScript 事件句柄。

一个简单的句柄

您需要使用标签将功能联系起来。您可以在 Quick Edit 视图中实现,如下步骤所示。

  1. 在视图左部分选择 oncomplete
  2. 在右部分,输入提示(您处于 oncomplete 事件句柄中)。

列表 4中划线的代码应产生在 Source 视图。

列表 4. 一个简单的事件句柄
<script type="text/javascript">
	function func_1(thisObj, thisEvent) {
		//use 'thisObj' to refer directly to this component instead of keyword 'this'
		//use 'thisEvent' to refer to the event generated instead of keyword 'event'
		alert("You are in the oncomplete event handler");
	}
</script>
.
.
.
<h:inputText id="text1" styleClass="inputText">
	<hx:inputHelperTypeahead id="typeahead1" styleClass="inputText_Typeahead" 
		value="#{myRoot.filter.search}" oncomplete="return func_1(this, event);">
	</hx:inputHelperTypeahead>
</h:inputText>

您可以在服务器端运行页面快速验证这些工作。当客户端收到推荐列表时,将会显示一个弹出信息框。在产生的评述中提到的 thisObj 参考了 Typeahead 关联的 DOM 输入栏。这对于获取维度、内容、id 等等十分有用。

Typeahead Behavior 对象

但是,更有用的是得到 JavaScript Typeahead Behavior 对象。Behavior 是 Extended Faces JavaScript 库中的概念,其功能可与 DOM 对象、事件和 Behavior 相关。这种情况下, DOM 对象是输入栏,事件是 onkeydown,行为类型是 typeahead,结果与列表 5 所示的代码相似。

列表 5. 得到 Typeahead 行为对象
var myTypeahead = hX_5.getBehaviorById(thisObj.id,"typeahead","onkeydown");

与对象相联允许您通过索引获取推荐字符。列表 6 展示了一个简单的句柄,它重复了推荐过程,添加到字符,之后提醒用户(图 11)。

列表 6. 获取、显示来自 Typeahead 的推荐列表的句柄
<script type="text/javascript">
	function func_1(thisObj, thisEvent) {
		var myTypeahead = hX_5.getBehaviorById(thisObj.id,"typeahead","onkeydown");
		var i, s = "The suggestions are: \n";
		for(i=0; i<myTypeahead.suggestionCells.length; i++)
		{
			s += (i+1) + ". " + myTypeahead.getSuggestion(i) + "\n";
		}
		alert(s);
	}
</script>
图 11. Event 中访问推荐内容
list of suggestions

用户所输入的内容

另一件事就是从输入栏中获取文本。在返回推荐列表后,会将第一个结果插入输入栏中。如果它与所输入的字符不同,输入栏的文本将会变为第一个推荐,在自动完成过程中高亮显示添加到输入栏中的推荐部分。 Typeahead 对象存储了用户输入的被称为 typedLength的字符长度。因此,您可以用代码从 Typeahead 中很容易的获取输入字符,如列表 7所示。

列表 7. 从 Typeahead Behavior 获取用户输入字符
var myTypeahead = hX_5.getBehaviorById(thisObj.id,"typeahead","onkeydown");
var typedSoFar = thisObj.value.substring(0,myTypeahead.typedLength);

改变外观

封装在 Rational Application Developer 中的 Enhanced JSF Components 最重要的特点之一就是它的高级自定义能力。这个部分中,您将学习到如何利用 Rational Application Developer 工具通过 CSS 和标签属性有效使用自定义能力。本文将会考察 Typeahead 三点不同的关系,并演示您如何为每个关系改变它的外观。包括了 JSP 代码片断和相应的 CSS 风格(可用的)。

非插入的下拉栏

Web 应用的 GUI (图形化用户接口)中的下拉栏常常是插入性的。 尤其是在您很可能不需要它的地方。大部分这样的脚本中, Typeahead 一般没用。例如,假设您正在输入您的名。一般来说不会有任何问题,因为名一般少于八个字符。当您不确定拼写的时候,您可以从 Typeahead 中获得帮助。之后,您就可以输入开始的几个字母并从列表中选择。另一个相似的情况为,Web 开发者不想使页面信息变得模糊,尤其是用户输入依赖于显示全部页面内容的情况。

一种减少 Typeahead 插入的方法是减少下拉栏的尺寸。有两种方法可以实现。第一,出现在下拉栏中的行数。这可以在 hx:inputHelperTypeahead 的 Properties 标签很容易的实现,只要在 Height of Suggestion Area中输入一个数字。您会注意到,还存在一个 Maximum number of suggestions displayed 区域(图 12)。这是用来限制从服务器端返回的结果数目。

发送 XML 结果列表的代码(列表 8)仅包括 n 个结果, n是返回的最大值。如果您设置的推荐列表高度数少于返回的数目,将会在下拉栏中自动显示垂直滚动条。以这种方式,不需要改变结果集的尺寸。

图 12. 限制下拉栏高度
setting Typeahead properties
setting Typeahead properties
列表 8. 结果尺寸的属性
<h:inputText id="text1" styleClass="inputText">
	<hx:inputHelperTypeahead id="typeahead1" styleClass="inputText_Typeahead" 
		value="#{myRoot.filter.search}" oncomplete="return func_1(this, event);" 
		size="2" maxSuggestions="100"></hx:inputHelperTypeahead>
</h:inputText>

第二种方式是通过激活 autocomplete 模式完全忽略 Typeahead 下拉栏 。当启动了 autocomplete 模式,不会在页面上显示下拉栏,但是您的输入自动变为来自服务器端的第一个结果。这种方式下, Typeahead 控件不会占用额外的屏幕空间。这对于结果集很小的情况十分有用,有时您会需要有多种选择。

例如,您正从联系人列表中输入名字。一般来说,输入第一个名字的姓的前两个字母就会出现唯一结果。不必折中从结果集中选择的功能,但是,您也可以利用键盘的方位键循环选择结果集中的内容。

第三种减少插入性的方式是引入透明的下拉栏。如果下拉栏降低了不透明度,您的注意力就不会从输入栏转移。如果下拉栏足够透明,您仍然可以看到页面信息,在输入栏输入信息。图 13显示了这种例子。

图 13. 透明的 Typeahead
you can see through the drop-down list
you can see through the drop-down list

列表 9 显示了用以产生下拉区域透明效果的 Typeahead 类的 CSS 风格。适合于透明度的属性存在于 bold中。

列表 9. CSS中的透明度
.inputText_TypeAhead {	
background-color: #ccddff;
	border-width: 1px;	
	border-style: solid;	
	border-color: ThreeDDarkShadow;	
	width:300px;	
	-moz-opacity: .7;	
	opacity: .7
	}

.inputText_TypeAhead-List {
	background-color: transparent;
	text-align: left;
	vertical-align: middle;
	height: auto;
	font-family: sans-serif;
	font-weight: 400;
	font-size: 10pt;
	border-collapse: collapse
}

.inputText_TypeAhead-Item {
	background-color: transparent;
	color: WindowText;
	padding-left: 1pt;
	padding-right: 1pt
}

使用长推荐列表的显示长度建议

如果您在输入栏中输入长字符往往是十分不方面的(例如,您需要输入很长的化合物名称或地址)。 Typeahead 可以在输入部分字符之后通过提供推荐列表的方式帮助您。对于平均长度为150个字符的数据集来说,这种功能是十分有效的,仅需前 <10 个字符就可以进行区分。

为了实现预定效果,有必要改变 Typeahead 的风格。虽然 Typeahead 能够处理限制文本,这是不需要的,尤其是当推荐限制多于一次的时候。取而代之的是,你可以通过 CSS 增加推荐区域的宽度 。默认情况下,推荐区域的宽度设为i输入栏的宽度。即使您 需要为长字符设置长输入栏,但是过长的输入栏不利于整齐或紧凑的设计。保证下拉栏不截断输入栏宽度的方式是在 hx:inputHelperTypeahead Properties 视图中清除 Match width of the suggestion area with the input field 检验框。在 Typeahead 标签设置 matchWidth=false

图 14. Matchwidth 检验框
changing the width in Properties
changing the width in Properties

下一步,您需要在 CSS 中指定宽度,使得 GUI 与图 15 相似。您可以使用任意 CSS 可接受的单位指定宽度。例如,y 设为400px。在基础 CSS 类中的 CSS width 属性是唯一一个需要改变的,如列表 10所示。

列表 10. CSS 中扩展宽度
.inputText_TypeAhead {	
background-color: #ccddff;
	border-width: 1px;	
	border-style: solid;	
	border-color: ThreeDDarkShadow;	
	width: 400px;	
	-moz-opacity: .7;	
	opacity: .7
	}
}
图 15. 宽的下拉栏
a dropdown wider than the field
a dropdown wider than the field

现在您可以自由的控制 Typeahead 的宽度,或完全(通过指定准确的宽度)或相对的包含元素(通过指定百分比)。

新字符

对于输入不知道的字符,或是难于输入的字符(例如长的包括字母和数字的字符)是比较棘手的。例如,这种字符可以是部分数字或 ID 号。这种情况下,最好在输入过程中尽早取消推荐列表需求。视图中有两栏用以控制取消推荐列表的 Ajax 需求的情况:

  • 一种是在您输入至少一种指定字符后取消延迟
  • 一种是控制从最后一个用户的键盘按下事件后所过去的总时间

这些属性---startCharactersstartDelay (如图 16所示)---的默认值为一个字符,500 毫秒。对于未知字符的情况,有必要将时间延迟减少为100ms。

图 16. 延迟属性
set the delay attributes
set the delay attributes

但是,如同所有的 Ajax,这样极大的增加了服务器的流量。建议您有效结合两种属性以优化性能和 Typeahead 可用性间的平衡关系。反之,当您十分熟悉所输入的字符时,根据同样的原因增大属性能够带来更多好处。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Rational, WebSphere, Web development
ArticleID=228399
ArticleTitle=Rational Application Developer V7.0 中 Typeahead 控件的高级用法
publish-date=06042007