有两种方式可以删除或插入链接。一种方法是查找 JSF 组件树中的动态元素的父组件,然后删除或插入元素。如果这些动态元素的父组件正在发生改变,就应该采用这种方法。另一种方法是直接将动态元素绑定到
Web 页面。这种方法比第一种方法简单,因为无需寻找 JSF 组件树中的父节点。不过,由于太过简便,这种方法具有一些限制:它只能用在所要删除或插入的元素具有固定的父组件并且在运行时之前父组件就已知的情况下。我之所以选用了这种方法(参见清单
5)是因为在本例中这些类别链接的父组件是固定的,并且也是预先定义了的。
category.jsp
<f:view>
<h:form id="helloForm">
<h:panelGrid id="title">
<h:outputText id = "hello_title" value="Inventory"/>
<a4j:outputPanel id = "book"
binding = "#{InventoryBean.categorygrid}"/>
</h:panelGrid>
</h:form>
</f:view>
public class InventoryBean implements InventoryListner {
private Category[] m_category;
public HtmlAjaxOutputPanel getCategorygrid() {
updateGUI();
return categorygrid;
public void setCategorygrid(HtmlAjaxOutputPanel categorygrid) {
this.categorygrid = categorygrid;
private void updateGUI(){
categorygrid.getChildren().clear();
if (m_category != null) {
int num = m_category.length;
for (int index = 0; index < num; index++) {
HtmlPanelGrid categorySubgrid =
JSFUtil.getLinkgrid("Bookstore_sublink" + index,
"#{InventoryBean.category[" +index+ "].categoryLabel}",
"#{InventoryBean.category[" +index+ "].onClickAction}");
categorygrid.getChildren().add(categorySubgrid);
如您所见,category.jsp 文件内的
updateGUI()
代码行就是要绑定受管
bean 中的动态元素。它清除了之前所创建的所有动态元素、基于新的数据模型创建了新的动态元素并将它们添加到预定义的父元素中。
将不同的行为绑定给不同的链接
现在,让我们探讨一下该如何将不同的类别细节信息绑定到不同的类别链接。我对一个数组进行了迭代,将每个元素转变为一个
GUI 组件,并将它插入到 JSF 组件树。我的思路是把所有类别插入到一个数组,其中每个类别作为数组中的一个元素。每个元素都有一个方法来返回其类别的标签,用另一个方法来绑定单击动作。通过让每个元素都具有可以将它与其他元素区分开来的独有的类别信息,我就能确保对于每个元素均只有一个惟一的行为被绑定到
“
onclick
” 动作。
在
updateGUI()
内,
"Bookstore_sublink"
+ index
是此类别链接的 ID。
"#{InventoryBean.category["
+ index+ "].categoryLabel}"
是此类别链接的标签。
"#{InventoryBean.category["
+ index+ "].onClickAction}"
是绑定到此类别链接的动作。
getCategoryLabel()
方法被用来返回链接标签,
onClickAction()
绑定单击动作(参见清单
清单 6. 值和动作绑定方法
public class Category {
private String category;
private ArrayList<BookItem> bookitems;
public String getCategoryLabel(){
if(bookitems.size() <2){
return bookitems.size() + " " + category;
}else{
return bookitems.size() + " " + category+"(s)";
public String onClickAction(){
HttpSession session =
(HttpSession)JSFUtil.getFacesContext().
getExternalContext().getSession(true);
session.setAttribute("CATEGORY", this);
return "success";
重定向 Web 页面
本节介绍了如何基于所单击的链接将用户重定向到一个新的页面。我使用 JSF 导航规则来重定向页面。
OnClickAction()
方法返回 “success” 以开始这个动作。通过发送到
Httpsession
的数据来为新页面提供内容。数据由受管 bean
DetailBean
从新页面的
Httpsession
检索。之后,
DetailBean
再相应地创建其 GUI 组件。
清单 7 给出了这些详细的实现。“detail.jsp” 是用户将被重定向到的新页面。
getDetailgrid()
是 detail.jsp 内的
DetailBean
的一部分,它被绑定到一个方法,该方法能创建此页面内的动态元素。在这个方法中,首先获得应该显示的类别数据,然后再使用
populate()
方法相应创建 GUI 内容。您可以研究
populate()
了解如何实时创建动态 GUI 元素,甚至进行页面布局。所有的页面信息都将由类别数据从
Httpsession
传递过来,所以,理论上讲,放入
Httpsession
的数据决定了新页面的外观。
清单 7. 将用户重定向到详细信息页面
detail.jsp
<f:view>
<h:form id="detailForm">
<h:panelGrid id="list">
<h:outputText id = "book_list" value="#{DetailBean.title}"/>
<h:panelGrid id = "detail" binding = "#{DetailBean.detailgrid}"/>
</h:panelGrid>
<h:commandButton id="back" value="Back" action="success"/>
</h:form>
</f:view>
public class DetailBean {
private HtmlPanelGrid detailgrid = null;
private Category cat;
public HtmlPanelGrid getDetailgrid() {
if(detailgrid == null){
detailgrid = new HtmlPanelGrid();
detailgrid.getChildren().clear();
HttpSession session =
(HttpSession)JSFUtil.getFacesContext().getExternalContext().getSession(true);
cat = (Category)session.getAttribute("CATEGORY");
session.removeAttribute("CATEGORY");
populate(detailgrid);
return detailgrid;
public void setDetailgrid(HtmlPanelGrid detailgrid) {
this.detailgrid = detailgrid;
private void populate(HtmlPanelGrid parent) {
if (cat != null) {
String category = cat.getCategory();
ArrayList<BookItem> items = cat.getBookitems();
if (category.equals("News paper")) {
//create GUI for News paper category.
}else if (category.equals("Magazine")) {
//create GUI for Magazine category.
}else{
//create GUI for other categories.
到目前为止,您已经了解了如何更新数据模型以及如何创建动态 GUI 元素。讨论了三个方面的内容 ― 如何插入元素以及如何从
Web 页面的合适位置删除元素、如何将不同的行为绑定到不同的元素以及如何重定向到一个 Web 页面。您不妨试着理解三者之间的关系,并针对自己的开发场景选择您所需要的部分。
在本节中,为了刷新页面的动态部分,我在图 2 所示的 “Bean” 和 “GUI” 层之间建立起了一种联系。我使用
RichFaces 的 Ajax4jsf 进行刷新。RichFaces 是一种开源框架,可以无需借助 JavaScript
即可将 Ajax 功能添加到现有的 JSF 应用程序。通过 Ajax4jsf,我克服了当前 JSF 不支持任何服务器端页面刷新的限制,而且我能满足只刷新所需内容的要求。
注册 RichFaces
安装 RichFaces 之后,通过添加清单 8 中的这些代码行可以更改
web.xml
文件来注册 RichFaces。
清单 8. 注册 RichFaces
<!-- Plugging the "Blue Sky" skin into the project -->
<context-param>
<param-name>org.richfaces.SKIN</param-name>
<param-value>blueSky</param-value>
</context-param>
<!-- Making the RichFaces skin spread to standard HTML controls -->
<context-param>
<param-name>org.richfaces.CONTROL_SKINNING</param-name>
<param-value>enable</param-value>
</context-param>
<!-- Defining and mapping the RichFaces filter -->
<filter>
<display-name>RichFaces Filter</display-name>
<filter-name>richfaces</filter-name>
<filter-class>org.ajax4jsf.Filter</filter-class>
</filter>
<filter-mapping>
<filter-name>richfaces</filter-name>
<servlet-name>Faces Servlet</servlet-name>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
在 Web 页面上所做的变更
注册了 RichFaces 之后,需要将来自清单 9 的那些标记添加到这个 category.jsp
文件以实现 “逆向 ajax”,即将数据从服务器端推到客户端并使用 Ajax 技术来刷新此页面。
清单 9. 将数据推到 Web 页面
<f:view>
<h:form id="helloForm">
<a4j:region>
<a4j:push reRender="book" eventProducer="#{InventoryBean.addListener}"/>
</a4j:region>
<h:panelGrid id="title">
<h:outputText id = "hello_title" value="Inventory"/>
<a4j:outputPanel id = "book" binding ="#{InventoryBean.categorygrid}"/>
<h:outputText id = "summary"
value="#{InventoryBean.categoryNumber}"></h:outputText>
</h:panelGrid>
</h:form>
</f:view>
请注意
a4j:push
标记。利用
eventProducer="#{InventoryBean.addListener}"
,此
Web 页面将一个侦听器注册到这个受管 bean,以便这个受管 bean 能够在需要的时候刷新 Web
页面。
reRender = "book"
意味着在服务器端数据被推到页面后,只有
ID 为 “book” 的组件才被刷新。
a4j:outputPanel
允许对页面区域进行标记,该页面区域通过
Ajax 响应被更新。
在受管 bean
内所做的更改
在这个受管 bean 内,应该注册
PushEventListener
以便一有推出事件发生就能将服务器端数据推出到客户端。借助
eventProducer
属性,可以将这个方法绑定到 Web 页面。推出事件由
categoryChanged()
方法中的
this.listener.onEvent(new
EventObject(this));
生成,每当服务器端数据发生改变时都会调用该方法。我之前谈论过
categoryChanged()
,清单 10 给出了它的具体实现。
清单 10. 注册 eventProducer
并推出数据
public class InventoryBean implements InventoryListner{
public void addListener(EventListener listener) {
synchronized (listener) {
if (this.listener != listener) {
this.listener = (PushEventListener) listener;
public void categoryChanged() {
refresh();
//code for refresh dynamic part via ajax
this.listener.onEvent(new EventObject(this));
现在,就可以从服务器端进行 Ajax 刷新了。将这种技巧与之前讨论的那些技巧结合起来,就能够将图 2
中所示的 “Database”、“Bean” 和 “GUI” 层连接起来。正如我已经讨论过的所有其他方法一样,这种方法也可以在任何适当的场合独立使用。
JSF 是一种十分方便的 Web 框架,可用来生成 HTML 页面、接收用户输入以及管理导航流。要在
JSF 内刷新一个页面,用户通常需要在 Web 页面上执行一些动作来生成 HTTP 请求,由 HTTP
响应回复该请求,进而导致页面的刷新。由服务器端触发 Web 页更新在 JSF 内并非易事。本文提供了这样一个解决方案,不仅能基于服务器端的请求自动更新
Web 页面,并且还可以同步服务器数据与 Web 页面的动态元素,这些动态元素在运行时创建并不断更改。
参阅文章 “
联合使用
CSS、JavaScript 和 JSF 精心打造 Ajax 应用程序,第 2 部分: 动态 JSF
表单
”,了解另外一种刷新动态元素的方法。
参阅 “
RichFaces
简介
”,了解如何向浏览器应用程序添加类似桌面的特性。
若要了解 Ajax4jsf,请参考 “
Ajax4jsf
Developer Guide
”。
developerWorks
技术活动
和
网络广播
:随时关注
developerWorks 技术活动和网络广播。
developerWorks Web development 专区:通过专门关于 Web 技术的文章和教程,扩展您在网站开发方面的技能。