Facelets 非常适合 JSF(第一部分)
以下内容转自互联网!
试图把 JSF 和 JSP 结合起来就像试图要把脚硬塞进手套一样:可能做得到,但是只是更好的解决办法出现之前的一个权宜之计。在这篇文章中,JSF 的热心支持者 Rick Hightower 介绍了关于 Facelets 他最喜欢的内容:容易的 HTML 样式的模板化和可重用的复合组件。
由于最近在 Java™ 服务器外观(JSF)项目上工作,我很有幸第一次使用了 Facelets。关于 Facelets,我最喜欢的是它让我可以创建可重用的复合组件。能够拿出一个页面(例如 JSP)并把它变成组件,对于我的 JSF 开发来说真是莫大的好处。我的结论是什么?如果不用 Facelets,那么就无法得到能从 JSF 获得的最大收获。
JSF 和 Java 服务器页面技术之间的不匹配,是 JSF 开发中的一个严重问题。问题是如何把 JSP 的动态内容集成到 JSF 基于组件的模型中。JSP 非常重视生成动态内容输出,而 JSF 需要 JSP 来协调组件模型的构建。因为这个任务超出了 JSP 原来的目的,所以产生了距离。
大多数 JSF 开发人员只是学会了一事一议地解决这类问题,但是这就像在锤子上放一个枕头,最终还会掉下来打伤脑袋。Facelets 是更加全面的解决方案:专为 JSF 组件模型度身定制的模板化语言。
Facelets 有以下吸引人的特性:
模板化(像 Tiles)
复合组件
定制的逻辑标记
表达式语言
对设计师友好的页面开发
创建组件库
这些特性比我想像的要更相关和统一。在这篇文章中,我讨论前两个:模板化和复合组件。我使用的 Web 应用程序基于为我的针对怀疑者的 JSF 系列开发的一个应用程序,我把它更新成使用 Facelets 视图而不是 Tiles。在进一步阅读之前,应当 下载示例代码。如果要随着讨论一起操作,还需要 安装 Facelets。
Facelets 概述
对于 Facelets 可能会做的最大一个错误假设,就是它只是 Tiles 的替代品。Facelets 远不止如此:它是思考 JSF 的新方式。
JSP 是种生成 servlet 的模板化语言。JSP 的主体与 servlet 的 doGet() 和 doPost() 方法等价(也就是说,成为 jspService() 方法)。JSF 定制标记(例如 f:view 和 h:form)只是调用 JSF 组件来呈现它们自己的当前状态。JSF 组件模型的生命周期独立于 JSP 生成的 servlet 的生命周期。这种独立性就是混淆的来源。
与 JSP 不同,Facelets 这个模板化语言,从构建之初,就考虑了 JSF 的组件生命周期。使用 Facelets,生成的模板会构建组件树,而不是 servlet。这就允许更好的重用,因为可以把组件组合成另一个组件。
Facelets 减少了编写定制标记才能使用 JSF 的需求。Facelets 本身就可以使用 JSF 定制组件。沟通 JSF 和 Facelets 只需要很少的特殊编码:要做的全部工作就是在 Facelet 标记库文件中声明 JSF 组件。在 Facelets 模板化语言中可以直接使用 JSF 组件,不用任何额外的开发。
选择标记
虽然多数开发人员把 Facelets 用于 XHTML,实际上这个框架并不在意使用什么标记:它与 XUL (XULFaces)兼容, Kito Mann 已经用它为 JSF 中心提供 RSS。
Facelets 模板框架
在提供针对组件构建设计的模板框架方面,Facelets 与 Tapestry (请参阅 参考资料)类似。但是,对于具有 JSP 背景的我们来说,Facelets 看起来比 Tapestry 友好得多。它允许使用熟悉的 JSTL 样式的标记和 JSTL/JSF/JSP 样式的表达式语言。大大降低的学习曲线意味着可以更加迅速地开始开发。
Facelets 允许定义能够直接包含进页面或者容易地添加到 Facelet 标记库的组件集。实际上让人高兴的是在 Facelets 中定义定制标记(复合组件和类似 JSP 定制标记的标记)的迅速。使用这些组件集,Facelets 还允许定义站点模板(和更小的模板)。这与使用 Tiles 很相似,但是少了定义文件。也可以在定制 JSF 组件内部使用 Facelets,因为 Facelets API 提供了可以容易地与 JSF 组件集成的接口。
Facelets 和 Tapestry
Facelets 与 Tapestry 很相似,可以相互比较。实际上,Tapestry 刚出现的时候,大大领先于它的时代,而 Facelets 确实借鉴了它的一些想法。但是,如果只把 Facelets 当成 JSF 版本的 Tapestry,那就错了。这两项技术是不同的。要了解关于 Tapestry 的更多内容,请参阅 Brett McLaughlin 两部分的系列 “了解 Tapestry,第 1 部分”。
Tiles 到 Facelets
如前所述,在这里使用的示例 Web 应用程序基于为我的 针对怀疑者的 JSF 系列创建的示例。它为一家在线 CD 店管理库存,创建、读取、更新和删除(CRUD)清单。它包含一个表单,让用户向系统输入新 CD,有一个单选按钮列表,允许用户选择音乐分类。当用户选择了一个分类时,就触发某些 JavaScript 立即把表单提交回服务器。应用程序还包含一个 CD 清单,用户可以根据标题或艺术家对清单中的 CD 排序。图 1 是应用程序类的 UML 图表:
图 1. 在线 CD 商店示例的类图
facelets/classes.gif' target='_blank'>facelets/classes.gif' border=0 οnlοad='javascript:if(this.width>screen.width-400)this.width=screen.width-400' alt='>
图 2 提供了商店的 CD 清单页面:
facelets/listing.gif' target='_blank'>facelets/listing.gif' border=0 οnlοad='javascript:if(this.width>screen.width-400)this.width=screen.width-400' alt='>
图 2. 在线 CD 商店的清单页面
facelets/listing.gif' target='_blank'>facelets/listing.gif' border=0 οnlοad='javascript:if(this.width>screen.width-400)this.width=screen.width-400' alt='>
原来的应用程序从 Tiles 得到视图支持,现在我将用 Facelets 构建视图。我先从用 Facelets 替换示例中的 Tiles 支持开始,然后编写复合组件。在开始之前,需要已经安装了 Facelets。
安装 Facelets
安装 Facelets 的步骤很容易。请注意,我假设已经下载并安装了 示例应用程序。
下载 Facelets 发行包 并解压缩。
把 jsf-facelets.jar 拷贝到 WEB-INF/lib 目录(在应用程序部署时,它最终必须放在 WEB-INF/lib 目录中)。
把 Facelet 初始化参数添加到 web.xml 文件中。
把 FaceletViewHandler 添加到 faces-config.xml 文件中。
步骤 1 和 2 比较基本。我将详细介绍其他两个步骤。
添加初始化参数
这一步假设已经安装了工作正常的 JSF 应用程序(例如 在线 CD 商店示例),然后编辑现有的 web.xml 页面,添加以下参数:
javax.faces.DEFAULT_SUFFIX
.xhtml
这告诉 JSF 采用 xhtml 前缀,Facelet 渲染器能够解释这个前缀。
Facelets 有许多参数,请参阅 参考资料 获得完整清单。如果示例有问题,请参考 DEVELOPMENT init 参数,它适合调试。把 REFRESH_PERIOD 参数设置为 low 在开发期间会有帮助。
添加 FaceletViewHandler
要让 Facelets 模板生效,需要把 Facelets 视图处理器告诉 JSF。JSF ViewHandler 是个插件,为不同的响应生成技术(包括 Facelets)处理 JSF 请求处理生命周期的 “渲染器响应和恢复视图” 阶段。(任何认为 JSF 不能扩展的人都是被误导了!)通过添加以下视图处理器到 faces-config.xml 中,就把 Facelets 插进了 JSF 中:
en
com.sun.facelets.FaceletViewHandler
用 Facelets 进行模板化
首先介绍 Facelets 模板化框架,因为它相对容易理解。创建和使用 Facelets 模板的步骤如下:
创建 layout.xhtml 页面。
定义 Facelet 的命名空间,导入对 Facelets 的使用。
用 ui:insert 标记定义页面的逻辑区域。
用纯文本和 ui:include 标记定义合理的默认值。
我要逐步介绍这些步骤,用在线 CD 商店清单页面作为我的布局示例。
步骤 1. 创建 layout.xhtml 页面
layout.xhtml 页面就是一个一般的 XHTML 文本文件,使用了以下文档类型声明:
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
' _fcksavedurl='http://www.w3.org/1999/xhtml">' target=_blank>http://www.w3.org/1999/xhtml">
不要求进一步细节!
步骤 2. 定义 Facelets 的命名空间
为了用 Facelets 标记进行模板化,需要用 XML 命名空间像下面这样导入它们:
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
http://www.w3.org/1999/xhtml"
xmlns:ui="facelets">' target=_blank>http://java.sun.com/jsf/facelets">
...
请注意 ui 命名空间的定义。
步骤 3. 用 ui:insert 标记定义页面的逻辑区域
下面,定义布局的逻辑区域,例如页面标题、小标题、导航、内容等等。下面是定义页面标题的示例:
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
http://www.w3.org/1999/xhtml"
xmlns:ui="facelets">' target=_blank>http://java.sun.com/jsf/facelets">
href="./css/main.css" type="text/css" rel="stylesheet" />
...
请注意使用 ui:insert 标记定义了标题的逻辑区域。ui:insert 元素内的文本 “Default title” 定义了模板用户不传递标题时显示的文本。也可以像下面这样编写上面的内容:
步骤 4. 用纯文本和 ui:includes 定义默认值
可以传递更多的纯文本作为默认值。例如,请研究 layout.xhtml 中的以下代码片段:
在这里,我用了 ui:insert 标记定义逻辑区域,用 ui:include 标记插入默认值。默认情况下,使用布局的页面会采用 header.xhtml 的内容作为标题文本,但是因为标题是 ui:insert 定义的逻辑区域,所以用这个模板也能传递不同的标题。对于拥有前端(例如,带有购物车的目录)和后端管理(例如添加新产品)的应用程序,后端站点在标题或导航上可能不同的链接。ui:include 标记可以容易地用新标题换掉默认标题。
清单 1 显示了示例应用程序的清单页面 list.xhtml 的完整代码:
清单 1. 完整的 list.xhtml
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
http://www.w3.org/1999/xhtml"
xmlns:ui="facelets">' target=_blank>http://java.sun.com/jsf/facelets">
href="./css/main.css" type="text/css" rel="stylesheet" />
现在已经知道了如何定义布局,我将介绍如何使用布局!
使用 Facelets 模板
为了调用模板,要使用 ui:composition 标记。为了把参数传递给模板,要使用 ui:define 标记,它是 ui:composition 标记的子元素。在清单 2 中,我调用了在线 CD 商店示例的布局页面:
清单 2. 调用布局页面
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
http://www.w3.org/1999/xhtml"
xmlns:ui="facelets"' target=_blank>http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
CD form
...
...
...
请注意在上面的调用中包含了以下命名空间:
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
有了 Facelets,就不必依赖 JSF 标记库,所以要使用核心和 HTML JSF 组件,必须通过以上命名空间导入它们。
html 标记的使用看起来可能有些奇怪。毕竟,清单 2 所示的布局页面要调用的模板已经有了一个 html 标记;所以这是不是意味着会得到两个 html 标记?如果真的这样,那么在 ui:composition 标记之外的内容全部被忽略,所以 html 标记所做的只是让 HTML 编辑器能够看到 HTML 片段。它不会影响运行时行为。
位置是一切
当页面调用布局模板时,只需指定模板的位置,如下所示:
这个标记调用清单 1 所示的模板,所以我要做的全部工作就是把参数传递给模板。然后,在复合标记内部,可以传递像标题这样的简单文本:
CD form
或者传递整个组件树:
...
...
...
请注意,在我定义和传递的许多逻辑区域中,cdForm.xhtml 只传递两个:内容和标题。
Tiles 与 Facelets
这篇文章带了三个示例:第一个使用 Tiles,第二个使用 Facelets,第三个使用复合组件。之所以包含 Tiles 示例是为了可以比较和对比两个框架中不同的模板化技术。请看看它,并看看自己有什么想法!
复合组件
如果只用 Facelets 定义和使用模板,那么可能会有点失败。虽然 Facelets 的模板化特性完整而且丰富,但是它没有 Tiles 之类的框架那么多的特性,后者还擅长定义默认值、相关模板的层次结构以及类似的东西。
但模板化不是 Facelets 真正出色的地方:Facelets 把它的精华放在复合组件上。(有趣的是,复合组件也给 Facelets 模板化带来了一些好处;例如,在 Facelets 中可以舍弃 f:verbatim 标记和各种 h:outputText 标记,因为所有的东西都被当成组件树中的组件。关于这方面的更多内容稍后介绍。)
对于这篇文章余下的部分,我将重点放在创建和使用复合组件的步骤上。但在开始之前,先要确保能够清楚地理解是什么让这些方便的小代码段这么棒。
破坏 DRY 原则
您是否曾经编写过像清单 3 所示的代码片段?
清单 3. 复合组件之前的生活
rowClasses="oddRow, evenRow" headerClass="tableHeader">
[
action="#{CDManagerBean.sort}">
action="#{CDManagerBean.sort}">
]
[
action="#{CDManagerBean.sort}">
action="#{CDManagerBean.sort}">
]
这段来自 listing.xhtml 的代码为示例应用程序的清单页面生成列标题和升序/降序排列链接。请注意,必须在多个地方重复代码,才能输出多列。(在上面的示例中您还会注意到,我在 ${..} 和 #{..} 之间切换;这可能让人迷惑,但它们做的是同样的事!)
所有这些渲染标题列和艺术家列的重复代码都破坏了 DRY 原则 —— 即,不要重复自己。那么您说,这里错在哪儿呢?假设在清单中平均有 5 列,应用程序中有 20 个不同的清单。使用清单 3 的方法,就不得不重复这 35 行代码 100 次,合计 3,500 行代码!维护所有这些代码会是种痛苦,但是如果决定修改清单的表示或添加一种通用的清单过滤方式,会怎么样?需要更多、更多的工作。
现在拿清单 3 和这个代码比较:
清单 4. 创建字段的新方式
rowClasses="oddRow, evenRow" headerClass="tableHeader">
看起来好像我只用 4 行代码就替代了 70 行或更多行代码!可以猜出,a:column 是个复合组件。在 Facelets 中,可以容易地定义这样的组件,如清单 5 所示:
清单 5. column.xhtml 渲染带有排序链接的列
" http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
http://www.w3.org/1999/xhtml"
xmlns:ui=" facelets"' target=_blank>http://java.sun.com/jsf/facelets"
xmlns:h=" http://java.sun.com/jsf/html"
xmlns:f=" http://java.sun.com/jsf/core"
xmlns:z=" http://www.qualcomm.com/jsf/core"
xmlns:c=" http://java.sun.com/jstl/core"
xmlns:fn=" http://java.sun.com/jsp/jstl/functions">
THIS TEXT WILL BE REMOVED
${label}
[
action="#{backingBean.sort}">
value="${fieldName}"/>
,
action="#{backingBean.sort}">
value="${fieldName}"/>
]
THIS TEXT WILL BE REMOVED AS WELL
要点
在进入更高级的示例之前,我想把您的注意力引到几件事上。首先,请注意在清单 5 中,我如何用一种通用方式引用值绑定:
第二,当调用这个复合组件时,我把 entity 和 fieldName 作为属性传递,如下所示:
Facelets 使用的 EL 规范允许用圆点(.)表示法或较少使用的映射表示法引用字段。例如,如果像上面那样调用,那么 ${entity[fieldName]} 等价于 CDManager.title。还请注意,我不需要 f:verbatim 标记或辅助的 h:outputText。对于您编写的任何 Facelets 页面,都可以这样。Facelets 知道 JSF 组件树,它的唯一目的就是构建这个组件树。这是使用 Facelets 与使用 JSP 和 Tiles 相比的另一个优势。
编写完成之后,就可以在其他许多地方使用 column.xhtml 复合组件。作为一个通用规则:如果正在破坏 DRY 原则,那么请考虑改用复合组件。
本文转自
http://www.hexiao.cn/jsf/read.php?fid=11&tid=30&searchword=facelets
以下内容转自互联网!
试图把 JSF 和 JSP 结合起来就像试图要把脚硬塞进手套一样:可能做得到,但是只是更好的解决办法出现之前的一个权宜之计。在这篇文章中,JSF 的热心支持者 Rick Hightower 介绍了关于 Facelets 他最喜欢的内容:容易的 HTML 样式的模板化和可重用的复合组件。
由于最近在 Java™ 服务器外观(JSF)项目上工作,我很有幸第一次使用了 Facelets。关于 Facelets,我最喜欢的是它让我可以创建可重用的复合组件。能够拿出一个页面(例如 JSP)并把它变成组件,对于我的 JSF 开发来说真是莫大的好处。我的结论是什么?如果不用 Facelets,那么就无法得到能从 JSF 获得的最大收获。
JSF 和 Java 服务器页面技术之间的不匹配,是 JSF 开发中的一个严重问题。问题是如何把 JSP 的动态内容集成到 JSF 基于组件的模型中。JSP 非常重视生成动态内容输出,而 JSF 需要 JSP 来协调组件模型的构建。因为这个任务超出了 JSP 原来的目的,所以产生了距离。
大多数 JSF 开发人员只是学会了一事一议地解决这类问题,但是这就像在锤子上放一个枕头,最终还会掉下来打伤脑袋。Facelets 是更加全面的解决方案:专为 JSF 组件模型度身定制的模板化语言。
Facelets 有以下吸引人的特性:
模板化(像 Tiles)
复合组件
定制的逻辑标记
表达式语言
对设计师友好的页面开发
创建组件库
这些特性比我想像的要更相关和统一。在这篇文章中,我讨论前两个:模板化和复合组件。我使用的 Web 应用程序基于为我的针对怀疑者的 JSF 系列开发的一个应用程序,我把它更新成使用 Facelets 视图而不是 Tiles。在进一步阅读之前,应当 下载示例代码。如果要随着讨论一起操作,还需要 安装 Facelets。
Facelets 概述
对于 Facelets 可能会做的最大一个错误假设,就是它只是 Tiles 的替代品。Facelets 远不止如此:它是思考 JSF 的新方式。
JSP 是种生成 servlet 的模板化语言。JSP 的主体与 servlet 的 doGet() 和 doPost() 方法等价(也就是说,成为 jspService() 方法)。JSF 定制标记(例如 f:view 和 h:form)只是调用 JSF 组件来呈现它们自己的当前状态。JSF 组件模型的生命周期独立于 JSP 生成的 servlet 的生命周期。这种独立性就是混淆的来源。
与 JSP 不同,Facelets 这个模板化语言,从构建之初,就考虑了 JSF 的组件生命周期。使用 Facelets,生成的模板会构建组件树,而不是 servlet。这就允许更好的重用,因为可以把组件组合成另一个组件。
Facelets 减少了编写定制标记才能使用 JSF 的需求。Facelets 本身就可以使用 JSF 定制组件。沟通 JSF 和 Facelets 只需要很少的特殊编码:要做的全部工作就是在 Facelet 标记库文件中声明 JSF 组件。在 Facelets 模板化语言中可以直接使用 JSF 组件,不用任何额外的开发。
选择标记
虽然多数开发人员把 Facelets 用于 XHTML,实际上这个框架并不在意使用什么标记:它与 XUL (XULFaces)兼容, Kito Mann 已经用它为 JSF 中心提供 RSS。
Facelets 模板框架
在提供针对组件构建设计的模板框架方面,Facelets 与 Tapestry (请参阅 参考资料)类似。但是,对于具有 JSP 背景的我们来说,Facelets 看起来比 Tapestry 友好得多。它允许使用熟悉的 JSTL 样式的标记和 JSTL/JSF/JSP 样式的表达式语言。大大降低的学习曲线意味着可以更加迅速地开始开发。
Facelets 允许定义能够直接包含进页面或者容易地添加到 Facelet 标记库的组件集。实际上让人高兴的是在 Facelets 中定义定制标记(复合组件和类似 JSP 定制标记的标记)的迅速。使用这些组件集,Facelets 还允许定义站点模板(和更小的模板)。这与使用 Tiles 很相似,但是少了定义文件。也可以在定制 JSF 组件内部使用 Facelets,因为 Facelets API 提供了可以容易地与 JSF 组件集成的接口。
Facelets 和 Tapestry
Facelets 与 Tapestry 很相似,可以相互比较。实际上,Tapestry 刚出现的时候,大大领先于它的时代,而 Facelets 确实借鉴了它的一些想法。但是,如果只把 Facelets 当成 JSF 版本的 Tapestry,那就错了。这两项技术是不同的。要了解关于 Tapestry 的更多内容,请参阅 Brett McLaughlin 两部分的系列 “了解 Tapestry,第 1 部分”。
Tiles 到 Facelets
如前所述,在这里使用的示例 Web 应用程序基于为我的 针对怀疑者的 JSF 系列创建的示例。它为一家在线 CD 店管理库存,创建、读取、更新和删除(CRUD)清单。它包含一个表单,让用户向系统输入新 CD,有一个单选按钮列表,允许用户选择音乐分类。当用户选择了一个分类时,就触发某些 JavaScript 立即把表单提交回服务器。应用程序还包含一个 CD 清单,用户可以根据标题或艺术家对清单中的 CD 排序。图 1 是应用程序类的 UML 图表:
图 1. 在线 CD 商店示例的类图
facelets/classes.gif' target='_blank'>facelets/classes.gif' border=0 οnlοad='javascript:if(this.width>screen.width-400)this.width=screen.width-400' alt='>
图 2 提供了商店的 CD 清单页面:
facelets/listing.gif' target='_blank'>facelets/listing.gif' border=0 οnlοad='javascript:if(this.width>screen.width-400)this.width=screen.width-400' alt='>
图 2. 在线 CD 商店的清单页面
facelets/listing.gif' target='_blank'>facelets/listing.gif' border=0 οnlοad='javascript:if(this.width>screen.width-400)this.width=screen.width-400' alt='>
原来的应用程序从 Tiles 得到视图支持,现在我将用 Facelets 构建视图。我先从用 Facelets 替换示例中的 Tiles 支持开始,然后编写复合组件。在开始之前,需要已经安装了 Facelets。
安装 Facelets
安装 Facelets 的步骤很容易。请注意,我假设已经下载并安装了 示例应用程序。
下载 Facelets 发行包 并解压缩。
把 jsf-facelets.jar 拷贝到 WEB-INF/lib 目录(在应用程序部署时,它最终必须放在 WEB-INF/lib 目录中)。
把 Facelet 初始化参数添加到 web.xml 文件中。
把 FaceletViewHandler 添加到 faces-config.xml 文件中。
步骤 1 和 2 比较基本。我将详细介绍其他两个步骤。
添加初始化参数
这一步假设已经安装了工作正常的 JSF 应用程序(例如 在线 CD 商店示例),然后编辑现有的 web.xml 页面,添加以下参数:
javax.faces.DEFAULT_SUFFIX
.xhtml
这告诉 JSF 采用 xhtml 前缀,Facelet 渲染器能够解释这个前缀。
Facelets 有许多参数,请参阅 参考资料 获得完整清单。如果示例有问题,请参考 DEVELOPMENT init 参数,它适合调试。把 REFRESH_PERIOD 参数设置为 low 在开发期间会有帮助。
添加 FaceletViewHandler
要让 Facelets 模板生效,需要把 Facelets 视图处理器告诉 JSF。JSF ViewHandler 是个插件,为不同的响应生成技术(包括 Facelets)处理 JSF 请求处理生命周期的 “渲染器响应和恢复视图” 阶段。(任何认为 JSF 不能扩展的人都是被误导了!)通过添加以下视图处理器到 faces-config.xml 中,就把 Facelets 插进了 JSF 中:
en
com.sun.facelets.FaceletViewHandler
用 Facelets 进行模板化
首先介绍 Facelets 模板化框架,因为它相对容易理解。创建和使用 Facelets 模板的步骤如下:
创建 layout.xhtml 页面。
定义 Facelet 的命名空间,导入对 Facelets 的使用。
用 ui:insert 标记定义页面的逻辑区域。
用纯文本和 ui:include 标记定义合理的默认值。
我要逐步介绍这些步骤,用在线 CD 商店清单页面作为我的布局示例。
步骤 1. 创建 layout.xhtml 页面
layout.xhtml 页面就是一个一般的 XHTML 文本文件,使用了以下文档类型声明:
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
' _fcksavedurl='http://www.w3.org/1999/xhtml">' target=_blank>http://www.w3.org/1999/xhtml">
不要求进一步细节!
步骤 2. 定义 Facelets 的命名空间
为了用 Facelets 标记进行模板化,需要用 XML 命名空间像下面这样导入它们:
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
http://www.w3.org/1999/xhtml"
xmlns:ui="facelets">' target=_blank>http://java.sun.com/jsf/facelets">
...
请注意 ui 命名空间的定义。
步骤 3. 用 ui:insert 标记定义页面的逻辑区域
下面,定义布局的逻辑区域,例如页面标题、小标题、导航、内容等等。下面是定义页面标题的示例:
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
http://www.w3.org/1999/xhtml"
xmlns:ui="facelets">' target=_blank>http://java.sun.com/jsf/facelets">
href="./css/main.css" type="text/css" rel="stylesheet" />
...
请注意使用 ui:insert 标记定义了标题的逻辑区域。ui:insert 元素内的文本 “Default title” 定义了模板用户不传递标题时显示的文本。也可以像下面这样编写上面的内容:
步骤 4. 用纯文本和 ui:includes 定义默认值
可以传递更多的纯文本作为默认值。例如,请研究 layout.xhtml 中的以下代码片段:
在这里,我用了 ui:insert 标记定义逻辑区域,用 ui:include 标记插入默认值。默认情况下,使用布局的页面会采用 header.xhtml 的内容作为标题文本,但是因为标题是 ui:insert 定义的逻辑区域,所以用这个模板也能传递不同的标题。对于拥有前端(例如,带有购物车的目录)和后端管理(例如添加新产品)的应用程序,后端站点在标题或导航上可能不同的链接。ui:include 标记可以容易地用新标题换掉默认标题。
清单 1 显示了示例应用程序的清单页面 list.xhtml 的完整代码:
清单 1. 完整的 list.xhtml
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
http://www.w3.org/1999/xhtml"
xmlns:ui="facelets">' target=_blank>http://java.sun.com/jsf/facelets">
href="./css/main.css" type="text/css" rel="stylesheet" />
现在已经知道了如何定义布局,我将介绍如何使用布局!
使用 Facelets 模板
为了调用模板,要使用 ui:composition 标记。为了把参数传递给模板,要使用 ui:define 标记,它是 ui:composition 标记的子元素。在清单 2 中,我调用了在线 CD 商店示例的布局页面:
清单 2. 调用布局页面
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
http://www.w3.org/1999/xhtml"
xmlns:ui="facelets"' target=_blank>http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
CD form
...
...
...
请注意在上面的调用中包含了以下命名空间:
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
有了 Facelets,就不必依赖 JSF 标记库,所以要使用核心和 HTML JSF 组件,必须通过以上命名空间导入它们。
html 标记的使用看起来可能有些奇怪。毕竟,清单 2 所示的布局页面要调用的模板已经有了一个 html 标记;所以这是不是意味着会得到两个 html 标记?如果真的这样,那么在 ui:composition 标记之外的内容全部被忽略,所以 html 标记所做的只是让 HTML 编辑器能够看到 HTML 片段。它不会影响运行时行为。
位置是一切
当页面调用布局模板时,只需指定模板的位置,如下所示:
这个标记调用清单 1 所示的模板,所以我要做的全部工作就是把参数传递给模板。然后,在复合标记内部,可以传递像标题这样的简单文本:
CD form
或者传递整个组件树:
...
...
...
请注意,在我定义和传递的许多逻辑区域中,cdForm.xhtml 只传递两个:内容和标题。
Tiles 与 Facelets
这篇文章带了三个示例:第一个使用 Tiles,第二个使用 Facelets,第三个使用复合组件。之所以包含 Tiles 示例是为了可以比较和对比两个框架中不同的模板化技术。请看看它,并看看自己有什么想法!
复合组件
如果只用 Facelets 定义和使用模板,那么可能会有点失败。虽然 Facelets 的模板化特性完整而且丰富,但是它没有 Tiles 之类的框架那么多的特性,后者还擅长定义默认值、相关模板的层次结构以及类似的东西。
但模板化不是 Facelets 真正出色的地方:Facelets 把它的精华放在复合组件上。(有趣的是,复合组件也给 Facelets 模板化带来了一些好处;例如,在 Facelets 中可以舍弃 f:verbatim 标记和各种 h:outputText 标记,因为所有的东西都被当成组件树中的组件。关于这方面的更多内容稍后介绍。)
对于这篇文章余下的部分,我将重点放在创建和使用复合组件的步骤上。但在开始之前,先要确保能够清楚地理解是什么让这些方便的小代码段这么棒。
破坏 DRY 原则
您是否曾经编写过像清单 3 所示的代码片段?
清单 3. 复合组件之前的生活
rowClasses="oddRow, evenRow" headerClass="tableHeader">
[
action="#{CDManagerBean.sort}">
action="#{CDManagerBean.sort}">
]
[
action="#{CDManagerBean.sort}">
action="#{CDManagerBean.sort}">
]
这段来自 listing.xhtml 的代码为示例应用程序的清单页面生成列标题和升序/降序排列链接。请注意,必须在多个地方重复代码,才能输出多列。(在上面的示例中您还会注意到,我在 ${..} 和 #{..} 之间切换;这可能让人迷惑,但它们做的是同样的事!)
所有这些渲染标题列和艺术家列的重复代码都破坏了 DRY 原则 —— 即,不要重复自己。那么您说,这里错在哪儿呢?假设在清单中平均有 5 列,应用程序中有 20 个不同的清单。使用清单 3 的方法,就不得不重复这 35 行代码 100 次,合计 3,500 行代码!维护所有这些代码会是种痛苦,但是如果决定修改清单的表示或添加一种通用的清单过滤方式,会怎么样?需要更多、更多的工作。
现在拿清单 3 和这个代码比较:
清单 4. 创建字段的新方式
rowClasses="oddRow, evenRow" headerClass="tableHeader">
看起来好像我只用 4 行代码就替代了 70 行或更多行代码!可以猜出,a:column 是个复合组件。在 Facelets 中,可以容易地定义这样的组件,如清单 5 所示:
清单 5. column.xhtml 渲染带有排序链接的列
" http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
http://www.w3.org/1999/xhtml"
xmlns:ui=" facelets"' target=_blank>http://java.sun.com/jsf/facelets"
xmlns:h=" http://java.sun.com/jsf/html"
xmlns:f=" http://java.sun.com/jsf/core"
xmlns:z=" http://www.qualcomm.com/jsf/core"
xmlns:c=" http://java.sun.com/jstl/core"
xmlns:fn=" http://java.sun.com/jsp/jstl/functions">
THIS TEXT WILL BE REMOVED
${label}
[
action="#{backingBean.sort}">
value="${fieldName}"/>
,
action="#{backingBean.sort}">
value="${fieldName}"/>
]
THIS TEXT WILL BE REMOVED AS WELL
要点
在进入更高级的示例之前,我想把您的注意力引到几件事上。首先,请注意在清单 5 中,我如何用一种通用方式引用值绑定:
第二,当调用这个复合组件时,我把 entity 和 fieldName 作为属性传递,如下所示:
Facelets 使用的 EL 规范允许用圆点(.)表示法或较少使用的映射表示法引用字段。例如,如果像上面那样调用,那么 ${entity[fieldName]} 等价于 CDManager.title。还请注意,我不需要 f:verbatim 标记或辅助的 h:outputText。对于您编写的任何 Facelets 页面,都可以这样。Facelets 知道 JSF 组件树,它的唯一目的就是构建这个组件树。这是使用 Facelets 与使用 JSP 和 Tiles 相比的另一个优势。
编写完成之后,就可以在其他许多地方使用 column.xhtml 复合组件。作为一个通用规则:如果正在破坏 DRY 原则,那么请考虑改用复合组件。
本文转自
http://www.hexiao.cn/jsf/read.php?fid=11&tid=30&searchword=facelets