JSF 2 简介,第 2 部分: 模板及复合组件
(转自: http://www.ibm.com/developerworks/cn/java/j-jsf2fu2/)
?
2009 年 6 月 25 日
模板和复合组件是 Java?Server Faces (JSF) 2 的两个功能强大的特性,借助这两个特性,您就可以实现易于修改和扩展的用户界面。在本文 — 共三部分的Facelets 和 JSF 2
在标准化开源 Facelets 实现的同时,JSF 2 专家组还对底层的 Facelets API 进行了更改,但保留了与标记库的后向兼容性。这意味着用开源 Facelets 所实现的现有视图均应适用于 JSF 2。
在 Rick Hightower 的这两篇文章 “Facelets 非常适合 JSF技巧 1:遵守 DRY 原则
在我作为软件开发人员从事的第一项工作中,我的任务是为基于 UNIX? 的计算机辅助设计和计算机辅助制造(CAD/CAM)系统实现一个 GUI。
最初,一切进行顺利,但是一段时间后,我的代码开始问题不断。待到代码发布的时候,系统已经相当脆弱,我甚至都害怕修复 bug,而这次的代码发布自然也伴随着一连串的 bug 报告。
如果我在这个项目中遵循了 DRY 原则 — 不重复自己—on't Repeat Yourself),我本可以让自己不至于这么悲惨。DRY 原则最初由 Dave Thomas 和 Andy Huntprinciple 提出(参见JSF 2 模板
JSF 2 在很多方面都支持 DRY 原则,其中之一就是通过模板。模板能够封装在应用程序视图中十分常见的功能,因此该功能只需被指定一次。在 JSF 2 应用程序中,一个模板可供多个组装(compositions)用于创建视图。
我在
与很多 Web 应用程序一样,这个 places 应用程序包含多个具有相同布局的视图。JSF 模板功能让您能够在一个模板内封装该布局 — 及其他共享工件,比如 JavaScript 和 Cascading Style Sheets(CSS)。清单 1 是
ui:insert name="windowTitle"> #{msgs.placesWindowTitle} </ui:insert> </title> </h:head> <h:body> <h:outputScript library="javascript" name="util.js" target="head"/> <h:outputStylesheet library="css" name="styles.css" target="body"/> <div style="font-size: 11px; font-weight: bold;">ui:insert name="heading"> #{msgs.placesHeading} </ui:insert> </div> <div style="font-size: 11px; font-weight: bold;">ui:insert name="menuLeft"/> </div> <div style="display: #{places.showContent}"> <ui:insert name="content"/> </div> <div style="font-size: 11px; font-weight: bold;">ui:insert name="menuRight"> <ui:include src="/sections/shared/sourceViewer.xhtml"/> </ui:insert> </div> </div> </h:body> </html>清单 1
template="/templates/masterLayout.xhtml"> <ui:define name="menuLeft"> <ui:include src="/sections/login/menuLeft.xhtml"/> </ui:define> <ui:define name="content"> <ui:include src="/sections/login/content.xhtml"/> </ui:define> </ui:composition>这个 login 视图为窗口的标题、头和右菜单使用了模板的默认内容。它只定义了特定于此 login 视图的功能:内容部分和左菜单。
通过为窗口标题、头或右菜单提供
template="/templates/masterLayout.xhtml"> <ui:define name="content"> <ui:include src="/sections/showSource/content.xhtml"/> </ui:define> <ui:define name="menuLeft"> <ui:include src="/sections/showSource/menuLeft.xhtml"/> </ui:define> <ui:define name="menuRight"> <ui:include src="/sections/showSource/menuRight.xhtml"/> </ui:define></ui:composition>source-viewer 视图定义了内容部分以及右菜单的内容。它还覆盖了由
template="/templates/masterLayout.xhtml"> <ui:define name="menuLeft"> <ui:include src="/sections/places/menuLeft.xhtml"/> </ui:define> <ui:define name="content"> <ui:include src="/sections/places/content.xhtml"/> </ui:define></ui:composition>JSF 2 模板功能
模板功能背后的概念十分简单。定义一个模板来封装在多个视图中常见的功能。每个视图由一个组装和一个模板组成。
当 JSF 创建视图时,它加载组装的模板,然后将由组装所定义的内容插入模板。
请注意清单
回页首技巧 2:使用组合的方式
在我的 CAD/CAM GUI 发布后不久,我花了几个月的时间与另一位开发人员 Bob 致力于一个新的项目。我们以 Bob 的代码为基础,而且不可思议地是,我们还能轻松进行更改并修复 bug。
我很快意识到 Bob 的代码和我的代码之间的最大区别是他编写了小
清单 5 显示了定义该菜单内容的文件:
清单 5. login 视图左菜单的实现清单 5
places 视图的左菜单的实现如清单 6 所示:
清单 6. places 视图的左菜单的实现清单 6
addressForm.xhtml"> <ui:include src="logoutIcon.xhtml"> </div> </ui:composition>清单 9 显示了 addressForm.xhtml:
清单 9. addressForm.xhtml清单 10 显示了 logoutIcon.xhtml:
清单 10. logoutIcon.xhtml
回页首技巧 3:牢记 LEGO 拼装玩具的理念
在我还是一个男孩的时候,我有两个最喜欢的玩具:一个是化学组合(chemistry set),一个是 LEGO 拼装玩具。这两种玩具让我能够通过组合基本的构建块来创建东西,而这也成为了我一生的爱好,只不过现在是打着软件开发的幌子。
JSF 的优势一直都在于其组件模型,但这种优势直到现在才完全实现,因为用 JSF 1 很难实现定制组件。您必须要编写 Java 代码、指定 XML 配置,并对 JSF 的生命周期有深刻的理解。有了 JSF 2,您就能够轻松实现定制组件:
无需配置、XML 或其他。无需 Java 代码。开发人员可以向其附加功能。修改后执行热部署。
在本文的剩余部分,我将向您介绍如何为 places 应用程序实现三个定制组件:一个图标、一个 login 面板和一个显示了地址地图和天气信息的面板。但是首先,让我先来概括介绍一下 JSF 2 复合组件。
实现定制组件
JSF 2 综合了
要使用复合组件,需要声明一个名称空间并使用标记。此名称空间通常为
/component/util"> ... <util:login.../> ...<html>而要使用
/components/util"> ... <util:icon.../> ...<html>最后,若要使用 place 组件,则可按如下所示的这样做:
/components/places"> ... <places:place.../> ...<html>
icon
每个图标都是一个链接。当用户单击
actionMethod="#{sourceViewer.showSource}" image="#{resource['images:disk-icon.jpg']}"/> ...</html>清单 12 给出了如何使用
actionMethod="#{places.logout}" image="#{resource['images:back-arrow.jpg']}"/> ...</html>清单 13 给出了
composite:interface> <composite:attribute name="image"/> <composite:attribute name="actionMethod" method-signature="java.lang.String action()"/> </composite:interface> <!-- IMPLEMENTATION --> <composite:implementation> <h:form> <h:commandLink action="#{cc.attrs.actionMethod}" immediate="true"> <h:graphicImage value="#{cc.attrs.image}" styleClass="icon"/> </h:commandLink> </h:form> </composite:implementation></html>与其他复合组件类似,清单 13
name="styleClass" default="icon" required="false"/> ... </composite:interface> <composite:implementation> ... <h:graphicImage value="#{cc.attrs.image}" stylestyle="font-size: 11px; font-weight: bold;">#{cc.attrs.styleClass}"/> ... </composite:implementation></html>在
如果不能指定
login
清单 15 显示了这个 places 应用程序是如何使用
component/util"> <util:login loginPrompt="#{msgs.loginPrompt}" namePrompt="#{msgs.namePrompt}" passwordPrompt="#{msgs.passwordPrompt}" loginAction="#{user.login}" loginButtonText="#{msgs.loginButtonText}" managedBean="#{user}"> <f:actionListener for="loginButton" type="com.clarity.LoginActionListener"/> </util:login> ...</html>清单 15
<composite:actionSource name="loginButton" targets="form:loginButton"/> <composite:attribute name="loginButtonText" default="Log In" required="true"/> <composite:attribute name="loginPrompt"/> <composite:attribute name="namePrompt"/> <composite:attribute name="passwordPrompt"/> <composite:attribute name="loginAction" method-signature="java.lang.String action()"/> <composite:attribute name="managedBean"/> </composite:interface> <!-- IMPLEMENTATION --> <composite:implementation> <h:form id="form" prependId="false"> <div value="#{cc.attrs.managedBean.name}"/> #{cc.attrs.passwordPrompt} <h:inputSecret id="password" value="#{cc.attrs.managedBean.password}" /> </panelGrid> <p> <h:commandButton id="loginButton" value="#{cc.attrs.loginButtonText}" action="#{cc.attrs.logi