| |
project dynamic faces是数个扩展javaserver faces技术的的项目之一。project dynamic faces是一个创新型的项目,提供了向基于javaserver faces技术的应用软件增添ajax功能的方法。它使我们能够让应用软件已经在使用的任何javaserver faces组件支持ajax功能。我们无需对组件进行修改就能够使它们支持ajax,我们也无需对应用软件进行任何修改就可以使它具有ajax的魔力。
要使应用软件具有ajax魔力,我们必须首先确定应用中希望ajax功能更新的网页部分。象基于javaserver faces技术的开发人员了解的那样,javaserver faces网页是由组件树表示的。利用dynamic faces,我们能够确定组件树中的哪个组件会受益于异步更新。就象使用ajax更新代表网页的html dom树的一部分那样,我们使用dynamic faces更新代表javaserver faces网页的组件树的一部分。因此,dynamic faces机制对于ajax和javaserver faces开发人员而言是熟悉的。
更重要的是,dynamic faces使用javaserver faces组件模式,使我们能够以一种更有效的方式利用ajax功能。由于组件模式的协作特性,一些网页组件上的javascript事件能够触发该网页上任何数量的其它组件的异步更新。dynamic faces使得这些异步更新只是向服务器发送的一次ajax请求的结果,而不是导致每次异步更新的ajax请求的结果。
dynamic faces还利用javaserver faces组件模式有效地管理客户机端和服务器端的状态。当dynamic faces更新客户机端上的组件状态时,它更新的只是已经改变的组件而不是整个树的状态。最好的一点是dynamic faces在后台完成所有这些操作,而且是以一种与javaserver faces技术的生命周期完全一致的方式完成的。
除了简化向应用软件增添ajax功能外,dynamic faces还向我们提供了增添ajax功能的方法的灵活性。这篇文章将讨论利用dynamic faces使应用软件更具交互性和活力的三种方法:
?利用dynamic faces提供的定制ajaxzone标签确定组件树中需要被ajax化的部分。
?利用dynamic faces提供的javascript库向单个组件增添ajax功能。
?在一个网页中增添支持ajax的组件,例如jmaki widget。
在学习这些技术前,我们先来看看应用软件如何才能使用dynamic faces技术。
开发利用dynamic faces的应用软件
通过向一个标准的javaserver faces 1.2实现中增添ajax功能,dynamic faces利用了javaserver faces技术的运行时间库的可扩展性。 dynamic faces的核心是定制的lifecycle和viewroot实现。这二个实现是javaserver faces技术提供的标准lifecycle和viewroot实现的扩展, 一个标准的lifecycle对象代表javaserver faces生命周期的一个实例,一个标准的viewroot对象代表一个组件树的根。联合使用定制lifecycle对象和定制viewroot对象,使javaserver faces生命周期能够处理ajax事务,在无需对整个网页更新的情况下重新显示组件树的一部分。这些定制实现服从于不支持ajax请求的标准实现。
为了使javaserver faces技术运行时间库知道定制lifecycle对象的存在,我们必须在配置描述器中利用一个初始化参数向facesservlet实例报告该对象。
<servlet> <servlet-name>faces servlet</servlet-name> <servlet-class>javax.faces.webapp.facesservlet</servlet-class> <init-param> <param-name>javax.faces.lifecycle_id</param-name> <param-value>com.sun.faces.lifecycle.partial</param-name> </init-param> <load-on-startup>1</load-on-startup> </servlet> |
此外,我们还必须将dynamic faces依赖的java archive(jar)文件添加到应用软件的web archive(war)文件的lib目录中。因为dynamic faces是基于java platform enterprise edition 5(java ee 5)的,我们所需要的几乎所有依赖关系都已经存在。最后一个依赖是shale remoting,dynamic faces利用它从java类路径中加载javascript文件和其它资源。 shale remoting依赖于commons-logging,因此我们必须向应用软件提供commons-logging。
最后,我们必须在使用它的每个网页中说明该dynamic faces标签库。 对于符合标准的非xml语法的javaserver pages(jsp)网页而言,这种说明如下所示:
| <%@ taglib prefix="jsfext" uri="http://java.sun.com/jsf/extensions/dynafaces" %> |
对于符合xml语法的javaserver pages(jsp)网页而言,这种说明如下所示:
<jsp:root xmlns:jsp="http://java.sun.com/jsp/page" version="1.2" xmlns:jsfext="http://java.sun.com/jsf/extensions/dynafaces" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> |
如果使用facelets而非jsp,语法与jsp xml的语法非常相似,如下所示:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:jsfext="http://java.sun.com/jsf/extensions/dynafaces" xmlns:f="http://java.sun.com/jsf/core"> |
好了。我们可以开始利用dynamic faces向应用软件中增添ajax功能了。
作为一种手动配置应用软件的替代性方案,我们可以使用dynamic faces下载包,其中包括面向jsp和facelets的空白应用软件。如果使用现成的空白应用软件,所有的设置工作已经预先完成,我们就可以开始编写网页了。
利用ajaxzone标签更新部分网页
确定网页上哪些组件将支持ajax的一种方式是用dynamic faces提供的ajaxzone定制标签封装它们。当这样做时,我们就告诉dynamic faces只异步更新使用ajaxzone标签确定的组件树部分。
例如,假设我们有一个带有一系列按钮的网页,客户在网上购买汽车时,可以用这些按钮选择标准或豪华装修。当客户点击一个按钮时,一系列其它组件的值会发生变化,而不会造成整个网页被更新。当客户改变一个组件的值时,汽车的价格也会改变,但不会造成整个网页的更新。
图1显示的是cardemo应用软件中网页的一个屏幕快照,它使客户能够选择汽车的配置情况。
 图1:选择汽车的配置 |
图1显示了二个由ajaxzone标签划分的区域。 当客户点击区域2中的一个按钮时,该区域中组件的值会发生变化。当客户改变区域2中的一个组件的值时,区域1中your price输出组件的值也会随之变化。
要实现这样的功能,我们需要将所有组件封装在ajaxzone标签中,如下所示:
<jsfext:ajaxzone id="zone1"> <h:panelgrid columns="2"> <h:outputtext styleclass="subtitle" value="#{bundle.basepricelabel}"/> <h:outputtext binding="#{carstore.currentmodel.components.baseprice}"/> <h:outputtext styleclass="subtitle" value="#{bundle.yourpricelabel}"/> <h:outputtext value="#{carstore.currentmodel.currentprice}"/> </h:panelgrid> </jsfext:ajaxzone> <jsfext:ajaxzone id="zone2" action="#{carstore.currentmodel.updatepricing}"> <h:commandbutton id="standard" value="#{bundle.standard}" styleclass="#{carstore.customizers.standard.buttonstyle}" actionlistener="#{carstore.choosepackage}"/> <h:commandbutton id="deluxe" value="#{bundle.deluxe}" styleclass="#{carstore.customizers.deluxe.buttonstyle}" actionlistener="#{carstore.choosepackage}"/> <h:outputtext value="#{bundle.engine}" styleclass="optionlabel"/> <h:selectonemenu styleclass="optionvalue" binding="#{carstore.currentmodel.components.engine}"/> <h:outputtext value="#{bundle.speakers}" styleclass="optionlabel"/> <h:selectoneradio styleclass="optionvalue" binding="#{carstore.currentmodel.components.speaker}"/> </jsfext:ajaxzone> |
前面的代码中包含名字分别为zone1和zone2的二个域。象代码显示的那样,zone 1中只有一个输出组件,因此它不会产生任何ajax请求; zone 2域中包含有输入和输出组件。当客户点击2个按钮中的1个,或者选择菜单或单选按钮列表中的一个选项时,zone2中的组件会启动一个ajax请求。这一请求将引起方法表达式#{carstore.currentmodel.updatepricing}中定义的操作被调用。
当使用域时,每个ajax事务将使得网页中的所有域被更新。前面例子的效果是,当用户选择zone 2中的任何输入组件时,ajax功能会自动地更新zone 1中的汽车价格数据。
值得指出的是,我们无需编写一行javascript代码就能够实现这一例子,我们也无需任何定制组件。这一应用软件使用了我们熟知的普通而简单的javaserver faces组件,但它们已经能够支持ajax功能了。
ajaxzone标签向网页创作者提供了一种使用dynamic faces的简单、熟悉、直观的方式。在最简单的例子中,ajaxzone标签能够向网页创作者提供所需要的功能。ajaxzone标签支持许多使我们能够进一步定制其操作的其它属性,ajaxzone文档中包含有其属性的完整清单。
下面的部分将讨论使用dynamic faces的另一种方法,它使我们能够细粒度地控制网页中组件的ajax化。
使用dynamic faces fireajaxtransaction方法
为了对与ajax相关的任务进行细粒度的控制,我们可以使用dynamic faces提供的内置javascript库。通过使用现有组件标签中合适的dynafaces.fireajaxtransaction javascript函数,我们可以对网页中组件异步更新方式有更细粒度的组件级控制。
例如,假设我们希望网页中的一些组件对一种类型的javascript事件━━例如onclick作出响应,并希望该网页中的其它组件对其它类型的javascript事件作出响应。又假设我们希望生成一个ajax请求的每个组件能够引起组件树的不同部分被异步更新。为了完成这些任务,我们需要使用fireajaxtransaction函数。
为了使用fireajaxtransaction函数,需要完成下面的准备工作:
?在一个组件标签中增添一个javascript事件属性,例如onclick。
?将该属性的值设置成dynafaces.fireajaxtransaction函数。
?向该函数传递一系列参数。
下面的代码是一个简单的hello world例子中一个网页的一部分,用户可以输入他或她的名字,点击一个按钮,应用程序会用一条包含用户名字的问候语响应用户的输入。
... <f:view> ... <h:form id="form" prependid="false"> ... <h:graphicimage value="wave.med.gif"/> <p> hello, my name is duke. what is your name? <p> <h:inputtext id="input" value="#{testbean.name}"/> <h:commandbutton id="button" actionlistener="#{testbean.changetext}" onclick="dynafaces.fireajaxtransaction(this, {execute: 'input', 'button', render: 'input', 'text'}); return false;" value="click"/> <p> <h:outputtext id="text" value="#{testbean.text}"/> </h:form> ... </f:view> |
在上面的例子中,inputtext标签代表一个输入域。当用户在该输入域中输入内容,并点击commandbutton标签表示的按钮,就会出现下面的情况:
1、dynafaces.fireajaxtransaction函数执行,使得dynamic faces向服务器发送一个ajax请求。
2、服务器返回一个dynamic faces javascript库处理的特别xml响应。
3、合适的库函数用新的值更新html dom树。
为了告诉fireajaxtransaction函数如何生成ajax请求,我们向它传输一系列参数。在上面的例子中,我们向fireajaxtransaction函数传递了2个参数。下面是调用fireajaxtransaction函数的代码:
onclick="dynafaces.fireajaxtransaction(this, {execute: 'input', 'button', render: 'input', 'text'}); return false;" |
this参数指的是代表触发该事件的按钮的标签,其它参数由指示dynamic faces如何处理该请求的选项组成。在这个例子中,选项是execute和render。
execute和render选项指的是javaserver faces生命周期的部分,如图2所示:
 图2: dynamic faces如何利用execute和render选项划分javaserver faces技术的生命周期 |
execute是在postback期间执行的生命周期部分。它包含有处理模式对象的数据转换、验证、更新阶段; render根据对网页的请求显示该网页。
在对fireajaxtransaction函数的调用中使用的execute选项列出了在javaserver faces生命周期的execute部分期间必须处理的组件的id,hello world例子中的这行代码如下所示。
execute: 'input', 'button'
接受用户名的input组件必须执行生命周期的execute部分,因为它的数据必须被保存到模式对象中。button组件也应当执行生命周期的execute部分,因为invoke application阶段是生命周期execute片断的一部分。
当生命周期的render片断显示一个使用dynamic faces的网页时,作为一次ajax请求的结果,它只显示该网页上被选定的组件。我们使用render选项显示要重新显示的组件的id,hello world例子中的render选项如下所示:
render: 'input', 'text'
在这一例子中,作为ajax请求的结果,生命周期的render片断会重新显示网页上的input和text组件。当用户点击该按钮时,代表input组件的输入字段和代表text组件的输出文本会被再次显示。输入字段会被重新显示,清除在点击该按钮前用户输入的值。输出文本会被重新显示,显示包含在点击该按钮前用户在输入域中输入的值的信息。该网页上的其它组件无需被重新显示。
除了execute和 render选项外,我们还可以使用其它选项,进一步定制dynamic faces处理事件的方式。需要记住的是,使用fireajaxtransaction函数使我们能够对网页中的哪些组件会得益于ajax有更多的控制。事实上,fireajaxtransaction函数让我们能够能够使网页中的任何组件支持ajax,而无需编写javascript或任何其它代码。
联合使用dynamic faces和jmaki
至此,我们已经学习了如何使用dynamic faces重新显示支持ajax的javaserver faces组件。但是,如何增添曾经在基于javaserver faces的应用软件中看到的支持ajax的widget呢?
我们可以利用project jmaki将喜欢的widget封装在javaserver faces组件中。这样,我们既能够享受到javaserver faces组件模式的好处,也能够获得使用被封装为javaserver faces组件的widget的灵活性。 同时,我们无须编写为现有组件实现ajax功能的javascript代码,以及为widget创建javaserver faces组件所要求的java平台代码。
如何联合使用jmaki和dynamic faces呢?对于网页创作者而言,这非常简单,就是将与jmaki widget相关的标签拖到网页中。 为了联合使用jmaki widget和dynamic faces,widget开发人员需要对jmaki widget的组件文件作一些小小的修改。这些修改使jmaki widget能够充分利用javaserver faces技术提供的组件状态管理系统,正确地转换dynamic faces要求的header。修改的细节超出了三篇文章的讨论范围,读者可以参阅相关资料。
dynamic faces开发团队已经完成了转换3个与dynamic faces.联合使用的jmaki widget所需要的工作。被转换的jmaki widget是script.aculo.us in-place editor widget、dojo fisheye widget、dojo inline-editor widget。
除了转换fisheye widget,开发团队还修改了jmaki api,使widget能够触发一个javaserver faces价值修改事件,如下图所示:
<a:ajax name="dojo.fisheye" value="#{fisheyebean.selectedindex}" valuechangelistener="#{fisheyebean.valuechanged}" args="{items:[ {iconsrc:'images/150x126_jalopy.jpg',caption:'jalopy',index:0}, {iconsrc:'images/150x126_luxury.jpg',caption:'luxury',index:1}, {iconsrc:'images/150x126_roadster.jpg',caption:'roadster',index:2}, {iconsrc:'images/150x126_suv.jpg',caption:'suv',index:3} ]}" /> |
dynamic faces和jmaki开发团队正在加紧工作,确保所有的jmaki widgets支持dynamic faces。
现在,让我们来站在网页创作者的角度来讨论这一问题。要想搞明白如何联合使用jmaki script.aculo.us in-place editor widget和 dynamic faces,我们在一个javaserver faces数据表组件中包含该widget,使我们能够编辑该表中一个单元的值。
在jsp网页中,我们必须说明要求的标签库,以及dynamic faces和jmaki标签,如下所示:
<%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%> <%@taglib prefix="jsfext" uri="http://java.sun.com/jsf/extensions/dynafaces"%> <%@taglib prefix="a" uri="http://java.sun.com/jmaki-jsf" %> |
下面的代码添加的是<jsfext:scripts />标签:
<f:view>
<html> <head> <title>table with jmaki</title> <jsfext:scripts /> </head> <body> |
这一标签显示dynamic faces所要求的javascript文件的<script>元素。
最后,通过包含一个指定被封装为jmaki widget 的in-place editor的 jmaki ajax标签,我们就将该widget添加到了网页上。
<h:form>
<h:datatable ... rows="10" binding="#{resultsetbean.data}" value="#{resultsetbean.list}" var="customer"> <h:column> <f:facet name="header"> <h:outputtext value="account id"/> </f:facet> <h:outputtext id="accountid" value="#{customer.accountid}"/> </h:column>
<h:column> <f:facet name="header"> <h:outputtext value="customer name"/> </f:facet> <a:ajax name="scriptaculous.inplace" value="#{customer.name}"/> </h:column> </h:datatable> ...
</h:form> </body> </html> </f:view> |
图3显示的是上述网页:
 图3:用户修改一个单元的值之前 |
图4显示的是当用户点击customer name列中一个单元的链接时的情况:
 图4: 用户点击customer name列中一个链接后的情况 |
需要注意的是,当用户点击customer name列中的一个元素时,客户名字元素会被一个输入组件、一个"ok"按钮、一个"cancel"链接所取代,使用户能够编辑当前的客户名字。如果用户点击"cancel"而不是"ok",该单元就会被重新显示为原来的值;如果用户输入一个值,并点击"ok"按钮,新的值就会被利用ajax技术发送到服务器,使该模式更新为新的值。 然后,被编辑的单元就会被重新显示。 图5显示的是是单元被修改后的网页:
 图5:在用户向服务器提交新的值后,网页重新显示为新的值 |
结论
project dynamic faces向我们提供了一种向基于javaserver faces的应用软件中添加ajax功能的灵活、有效的方式,而无需放弃javaserver faces组件模式的任何优势。在内置的javascript库、ajax实现、dynamic faces提供的组件交互模式的帮助下,我们会发现利用dynamic faces添加ajax功能更容易了。基于javaserver faces的应用软件能够得益于jmaki widget提供的更多的灵活性。
|
|