`

JSF 2 简介,第 2 部分: 模板及复合组件

阅读更多

(转自: http://www.ibm.com/developerworks/cn/java/j-jsf2fu2/

 

2009 年 6 月 25 日

模板和复合组件是 Java™Server Faces (JSF) 2 的两个功能强大的特性,借助这两个特性,您就可以实现易于修改和扩展的用户界面。在本文 — 共三部分的  系列文章   的第 2 部分 — 中,JSF 2 专家组成员 David Geary 将向您展示如何在您的 Web 应用程序中利用模板和复合组件。

早在 2000,当我还是 JavaServer Pages(JSP)邮件列表中的一个活跃分子的时候,我遇到了 Craig McClanahan,当时他正忙着开发一个新的 Web 框架,称为 Struts。在那时,我还正在从 Swing 转向服务器端 Java 编程,所以我已经实现了一个很小的框架来分离 JSP 视图布局及其内容,这非常类似于 Swing 布局管理器的理念。Craig 问我,是否愿意将我的模板   库包含在 Struts 内,我欣然同意了。这样一来,与 Struts 1.0 捆绑的 Struts Template Library 遂成为了 Struts 流行的 Tiles 库的基础,而 Tiles 库最终成为了一个顶级的 Apache 框架。

JSF 2 现在的默认显示技术 — Facelets — 就是一个模板框架,在很大程度上基于的是 Tiles。JSF 2 还提供了一个功能强大的机制,称为复合组件 ,该机制构建在 Facelets 的模板特性之上,因此,在无需任何 Java 代码和 XML 配置的情况下就可以实现定制组件。在本文中,我将向您介绍模板和复合组件,并且还会给出如何充分利用 JSF 2 的三个技巧:

  • 技巧 1:遵守 DRY 原则
  • 技巧 2:使用组合的方式
  • 技巧 3:牢记 LEGO 拼装玩具的理念
Facelets 和 JSF 2

在标准化开源 Facelets 实现的同时,JSF 2 专家组还对底层的 Facelets API 进行了更改,但保留了与标记库的后向兼容性。这意味着用开源 Facelets 所实现的现有视图均应适用于 JSF 2。

在 Rick Hightower 的这两篇文章 “Facelets 非常适合 JSF   ” 和 “高级 Facelets 编程 ” 中可以找到有关 Facelets 众多特性的更多信息。

技巧 1:遵守 DRY 原则

在我作为软件开发人员从事的第一项工作中,我的任务是为基于 UNIX® 的计算机辅助设计和计算机辅助制造(CAD/CAM)系统实现一个 GUI。

最初,一切进行顺利,但是一段时间后,我的代码开始问题不断。待到代码发布的时候,系统已经相当脆弱,我甚至都害怕修复 bug,而这次的代码发布自然也伴随着一连串的 bug 报告。

如果我在这个项目中遵循了 DRY 原则 — 不重复自己(Don't Repeat Yourself),我本可以让自己不至于这么悲惨。DRY 原则最初由 Dave Thomas 和 Andy Huntprinciple 提出(参见  参考资料 ),它要求:

每条知识都必须在系统内具有一个单一、清晰和权威的表示。

我的 CAD/CAM 应用程序并不符合 DRY 原则 — 它具有太多关注点之间的交叉 — 因此在一个地方所做的更改常常会在其他地方引起意想不到的更改。

JSF 1 在几个方面违背了 DRY 原则,比如,它强迫您提供托管 beans 的两种表示 — 一个使用 XML,一个使用 Java 代码。对多重表示的需求让创建和更改托管 bean 更加困难。正如我在本系列  第 1 部分   中介绍的,JSF 2 让您能够使用注释取代 XML 来配置托管 bean,这样一来,托管 bean 就具有了一个单一、权威的表示。

除托管 beans 之外,就连一些看似有益的实践 — 比如在所有视图中包括相同的样式表 — 也违背了 DRY 原则,并会导致混乱。比如,如果要更改样式表的名字,就必须更改多个视图。如果可能,最好是封装此样式表包含。

DRY 原则同样适用于代码设计。如果多个方法均包含遍历树的代码,一种好的做法是(比如在一个子类中)封装遍历树的算法。

在实现 UI 时,因大多数更改均在开发过程中发生,所以遵守 DRY 原则尤其重要。

JSF 2 模板

JSF 2 在很多方面都支持 DRY 原则,其中之一就是通过模板 。模板能够封装在应用程序视图中十分常见的功能,因此该功能只需被指定一次。在 JSF 2 应用程序中,一个模板可供多个组装(compositions) 用于创建视图。

我在  第 1 部分   中所介绍的 places 应用程序具有三个视图,如图 1 所示:


图 1. places 应用程序的视图:Login、source viewer 和 places

places 应用程序的视图places 应用程序的图标places 应用程序的图标

与很多 Web 应用程序一样,这个 places 应用程序包含多个具有相同布局的视图。JSF 模板功能让您能够在一个模板内封装该布局 — 及其他共享工件,比如 JavaScript 和 Cascading Style Sheets(CSS)。清单 1 是  图 1   中所示的这三个视图的模板:


清单 1. places 模板:/templates/masterLayout.xhtml
				
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
   xmlns:ui="http://java.sun.com/jsf/facelets">

  <h:head>
    <title>
      <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 class="pageHeading">
      <ui:insert
 name="heading">
        #{msgs.placesHeading}
      </ui:insert>     
    </div> 
      
    <div class="menuAndContent"> 
      <div class="menuLeft"> 
        <ui:insert
 name="menuLeft"/>
      </div>    
	    
      <div class="content" style="display: #{places.showContent}">
        <ui:insert
 name="content"/>
      </div> 
	    
      <div class="menuRight">
        <ui:insert
 name="menuRight">
          <ui:include src="/sections/shared/sourceViewer.xhtml"/>
        </ui:insert>
      </div> 
    </div>  
  </h:body> 
</html>

清单 1   中的模板为此应用程序的所有视图提供了如下的基础设施:

  • HTML  <head><body>    <title>
  • 一个默认标题(可由使用此模板的那些组装覆盖)
  • 一个 CSS 样式表
  • 某些实用 JavaScript
  • 一个布局,格式为  <div> ,以及对应的 CSS 类
  • 头的默认内容(可被覆盖)
  • 右菜单的默认内容(可被覆盖)

正如  清单 1   所示,模板通过  <ui:insert>   标记将内容插入到布局中。

如为  <ui:insert>   标记指定了主体,正如我在  清单 1   中为窗口标题、头和右菜单所做的,JSF 会将此标记的主体作为默认内容 。借助  <ui:define>   标记,使用此模板的那些封装可以定义内容或者覆盖默认内容,如清单 2 所示,它给出了 login 视图的标记:


清单 2. login 视图
				
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
   xmlns:ui="http://java.sun.com/jsf/facelets"
   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 视图的功能:内容部分和左菜单。

通过为窗口标题、头或右菜单提供  <ui:define>   标记,我也可以覆盖此模板的默认内容。比如,清单 3 显示了这个 source-viewer 视图(图 1   中间的图片):


清单 3. source-viewer 视图
				
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
   xmlns:ui="http://java.sun.com/jsf/facelets"
   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 视图定义了内容部分以及右菜单的内容。它还覆盖了由  清单 1   中的模板定义的针对左菜单的默认内容。

清单 4 显示了 places 视图(图 1   底部的图片):


清单 4. places 视图
				
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
   xmlns:ui="http://java.sun.com/jsf/facelets"
   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 创建视图时,它加载组装的模板,然后将由组装所定义的内容插入模板。

请注意清单  23    4   之间的相似性。所有这三个视图均指定模板并定义内容。另外,也请注意创建新视图十分容易,因为大多数视图的基础设施都封装在模板及所包含的文件内。

使用 JSF 模板功能的另一个有趣之处是类似清单  23    4   中的这些视图并不会随时间有太多变化,所以大部分视图代码基本不需要维护。

与使用模板的视图类似,模板本身也更改甚少。由于大量常见功能都封装在几乎不用维护的代码中,这样一来,您就可以将精力集中于视图的实际内容 — 比如,login 页面的左菜单应该有些什么内容。专心于视图的实际内容就是下一个技巧的主旨所在。





回页首


技巧 2:使用组合的方式

在我的 CAD/CAM GUI 发布后不久,我花了几个月的时间与另一位开发人员 Bob 致力于一个新的项目。我们以 Bob 的代码为基础,而且不可思议地是,我们还能轻松进行更改并修复 bug。

我很快意识到 Bob 的代码和我的代码之间的最大区别是他编写了   方法 — 通常是在代码的 5 至 15 行之间 — 并且他的整个系统都是由这些小方法拼接而成的。在我还在忙着修改我之前项目中具有很多关注点的长方法时,Bob 已经开始机敏地组合小方法和原子功能性了。Bob 的代码和我的代码在维护性和可扩展性方面自然也有着天壤之别,从那以后,我开始信服小方法。

虽然 Bob 和我那时都没有意识到,但是我们过去一直在使用 Smalltalk 的一种设计模式,称为 Composed Method(参见  参考资料 ):

在一个抽象级别,将软件分成能执行单个任务的多个方法。

使用 Composed Method 模式的好处已经有大量书面记载(详细说明,请参见 Neal Ford 的 “演化架构与紧急设计:组合方法和 SLAP ” )。在这里,我将侧重于介绍如何在 JSF 视图中使用 Composed Method 模式。

JSF 2 鼓励使用较小的视图段组装视图。模板封装了常见功能,进而将视图分成了更小的块。JSF 2 还提供了一个  <ui:include>   标记,正如我在先前的代码清单中所展示的,这个标记可以让您将视图进一步分成更小的功能块。比如,图 2 展示了 places 应用程序的 login 页面的左菜单:


图 2. login 页面的左菜单
login 视图的左菜单

清单 5 显示了定义该菜单内容的文件:


清单 5. login 视图左菜单的实现
				
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:ui="http://java.sun.com/jsf/facelets">

  <div class="menuLeftText">
    #{msgs.welcomeGreeting}

    <div class="welcomeImage">
      <h:graphicImage library="images" name="cloudy.gif"/>
    </div>
  </div>
    
</html>

清单 5   内的标记很简单,这就使文件更易于阅读、理解、维护和扩展。如果相同的代码埋藏在一个很长的、包含实现 login 视图所需的全部内容的 XHTML 页面内,那么它更改起来将会很繁琐。

图 3 显示了 places 视图的左菜单:


图 3. places 视图的左菜单
places 视图的左菜单

places 视图的左菜单的实现如清单 6 所示:


清单 6. places 视图的左菜单的实现
				
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:util="http://java.sun.com/jsf/composite/components/util">

  <div class="placesSearchForm"> 
    <div class="placesSearchFormHeading">
      #{msgs.findAPlace}
    </div>    
	
    <h:form prependId="false">
      <h:panelGrid columns="2">
	   
        #{msgs.streetAddress}
        <h:inputText value="#{place.streetAddress}" size="15"/>
	     
        #{msgs.city}  <h:inputText value="#{place.city}"  size="10"/>
        #{msgs.state} <h:inputText value="#{place.state}" size="3"/>
        #{msgs.zip}   <h:inputText value="#{place.zip}"   size="5"/>
  	     
        <h:commandButton value="#{msgs.goButtonText}" 
          style="font-family:Palatino;font-style:italic"
          action="#{place.fetch}"/>
	       	            
      </h:panelGrid>	        
    </h:form>
  </div>
	
  <util:icon image
="#{resource['images:back.jpg']}"
    actionMethod
="#{places.logout}"
    style="border: thin solid lightBlue"/>

</ui:composition>

清单 6   实现了一个表单,并且此表单使用了一个图标组件。(我随后会在  图标组件   一节对该图标组件进行详细讨论。目前,只需知道页面作者可以用一个图标关联图像和方法。)这个 logout 图标的图像显示在  图 3   的底部,而此 logout 图标的方法 —  places.logout()   — 则如清单 7 所示:


清单 7.  Places.logout()   方法
				
package com.clarity;
...
@ManagedBean()
@SessionScoped

public class Places {
  private ArrayList<Place> places = null;
  ...  
  private static SelectItem[] zoomLevelItems = {
  ... 
  public String logout() {
    FacesContext fc = FacesContext.getCurrentInstance();     
    ELResolver elResolver = fc.getApplication().getELResolver();
	     
    User user = (User)elResolver.getValue(
      fc.getELContext(), null, "user");
	
    user.setName("");
    user.setPassword("");
	   
    setPlacesList(null);

    return "login"; 
  }
}

对我而言,清单 6  — places 视图的左菜单的实现 — 已经十分接近 30 行的代码长度限制。此清单有点难于读懂,并且该代码片段内的表单和图标可被重构成各自的文件。清单 8 显示了  清单 6   的重构版,其中,表单和图标被封装进各自的 XHTML 文件:


清单 8. 重构 places 视图的左菜单
				
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets">

  <div class="placesSearchForm"> 
    <div class="placesSearchFormHeading">
      #{msgs.findAPlace}
    </div>    

    <ui:include src="addressForm.xhtml
">	
    <ui:include src="logoutIcon.xhtml
">	
  </div>    

</ui:composition>

清单 9 显示了 addressForm.xhtml:


清单 9. addressForm.xhtml
				
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets">
    
  <h:form prependId="false">
    <h:panelGrid columns="2">
    
      #{msgs.streetAddress}
      <h:inputText value="#{place.streetAddress}" size="15"/>
      
      #{msgs.city}  <h:inputText value="#{place.city}"  size="10"/>
      #{msgs.state} <h:inputText value="#{place.state}" size="3"/>
      #{msgs.zip}   <h:inputText value="#{place.zip}"   size="5"/>
      
      <h:commandButton value="#{msgs.goButtonText}" 
        style="font-family:Palatino;font-style:italic"
        action="#{place.fetch}"/>
                     
    </h:panelGrid>         
  </h:form>
  
</ui:composition>

清单 10 显示了 logoutIcon.xhtml:


清单 10. logoutIcon.xhtml
				
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:util="http://java.sun.com/jsf/composite/components/util">

  <util:icon image="#{resource['images:back.jpg']}"
    actionMethod="#{places.logout}"
    style="border: thin solid lightBlue"/>

</ui:composition> 

在从多个小文件组装视图时,就可享受到 Smalltalk 的 Composed Method 模式的益处。您还可以组织这些文件以便更易于对更改做出反应。例如,图 4 显示了构成 places 应用程序内的这三个视图的文件:


图 4. places 应用程序的视图
places 应用程序的视图 

我所创建的这三个目录 — views、sections 和 templates — 包含了用来实现 places 应用程序视图的大多数 XHTML 文件。由于 views 和 templates 目录内的文件很少更改,因此我更多关注的是 sections 目录。例如,若我想要更改 login 页面左菜单内的图标,我就知道该到哪里去更改:sections/login/menuLeft.xhtml。

当然,您可以使用任何目录结构来组织您的 XHTML 文件。如果组织得合理,定位想要修改的代码就会非常容易。

除了遵循 DRY 原则和使用 Composed Method 模式之外,还有一种好的做法是在定制组件内封装功能。组件是一种功能强大的重用机制,而且您应该充分利用这种强大性。与 JSF 1 不同,使用 JSF 2 更易于实现定制组件。





回页首


技巧 3:牢记 LEGO 拼装玩具的理念

在我还是一个男孩的时候,我有两个最喜欢的玩具:一个是化学组合(chemistry set),一个是 LEGO 拼装玩具。这两种玩具让我能够通过组合基本的构建块来创建东西,而这也成为了我一生的爱好,只不过现在是打着软件开发的幌子。

JSF 的优势一直都在于其组件模型,但这种优势直到现在才完全实现,因为用 JSF 1 很难实现定制组件。您必须要编写 Java 代码、指定 XML 配置,并对 JSF 的生命周期有深刻的理解。有了 JSF 2,您就能够轻松实现定制组件:

  • 无需配置、XML 或其他。
  • 无需 Java 代码。
  • 开发人员可以向其附加功能。
  • 修改后执行热部署。

在本文的剩余部分,我将向您介绍如何为 places 应用程序实现三个定制组件:一个图标、一个 login 面板和一个显示了地址地图和天气信息的面板。但是首先,让我先来概括介绍一下 JSF 2 复合组件。

实现定制组件

JSF 2 综合了  Facelets 模板 、资源处理(在  第 1 部分   中讨论过)和一个简单的命名约定来实现复合组件 。复合组件,正如其名字所示,让您能够从现有组件组装一个新组件。

一般情况下,是在 resources 目录下的 XHTML 内实现复合组件,并将它们完全通过约定链接到一个名称空间和标记。图 5 展示了我是如何为 places 应用程序组织这些复合组件的:


图 5. places 应用程序的组件
places 应用程序的组件

要使用复合组件,需要声明一个名称空间并使用标记。此名称空间通常为  http://java.sun.com/jsf/composite   外加目录名,这个目录就是 resources 目录下组件所在之处。组件名本身是其 XHTML 文件的名字,只不过没有 .xhtml 扩展名。这种约定消除了对配置的需要。比如,要在 places 应用程序中使用  login   组件,应该这样做:

<html xmlns="http://www.w3.org/1999/xhtml"
    ...
    xmlns:util="http://java.sun.com/jsf/composite/component/util
">
  ...
  <util:login
.../>
  ...
<html>

而要使用  icon   组件,则需要像下面这样:

<html xmlns="http://www.w3.org/1999/xhtml"
    ...
    xmlns:util="http://java.sun.com/jsf/composite/components/util
">
  ...
  <util:icon
.../>
  ...
<html>

最后,若要使用 place 组件,则可按如下所示的这样做:

<html xmlns="http://www.w3.org/1999/xhtml"
    ...
    xmlns:util="http://java.sun.com/jsf/composite/components/places
">
  ...
  <places:place
.../>
  ...
<html>

icon   组件:一个简单的复合组件

places 应用程序使用了图 6 所示的这两个图标:


图 6. places 应用程序的图标

places 应用程序的图标  places 应用程序的图标

每个图标都是一个链接。当用户单击  图 6   左侧的图标时,JSF 就会显示当前视图的标记,而激活右侧图标则会使用户登出此应用程序。

可以为链接指定一个 CSS 类名和图像,并且还可以向链接附加方法。当用户单击一个被关联的链接时,JSF 就会调用那些方法。

清单 11 给出了  icon   组件是如何被用来在 places 应用程序中显示标记的:


清单 11. 使用  icon   组件显示标记
				
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:util="http://java.sun.com/jsf/composite/components/util">

  <util:icon actionMethod
="#{sourceViewer.showSource
}" 
                      image
="#{resource['images:disk-icon.jpg
']}"/>
  ...
</html>


清单 12 给出了如何使用  icon   组件执行登出:


清单 12. 使用  icon   组件执行登出
				
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:util="http://java.sun.com/jsf/composite/components/util">

  <util:icon actionMethod
="#{places.logout
}" 
                      image
="#{resource['images:back-arrow.jpg
']}"/>
  ...
</html>

清单 13 给出了  icon   组件的代码:


清单 13.  icon   组件
				
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:composite="http://java.sun.com/jsf/composite">
    
  <!-- INTERFACE -->
  <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   中的  icon   组件包含两节:<composite:interface>    <composite:implementation><composite:interface>   节定义了一个界面,可用来配置此组件。icon   组件具有两个属性:image    actionMethod ,前者定义了组件的外观,后者定义了组件的行为。

<composite:implementation>   节包含组件的实现。它使用  #{cc.attrs.ATTRIBUTE_NAME }   表达式来访问组件的界面内定义的属性。(cc   是 JSF 2 表达式语言中的保留关键字,代表的是复合组件。)

请注意,清单 13   中的  icon   组件用  <h:graphicImage>    styleClass   属性为其图像指定了一个 CSS 类。该 CSS 类的名字被硬编码为  icon ,所以您就能够指定一个具有该名称的 CSS 类,JSF 将为应用程序中的所有图标使用该类。但是如果您想要覆盖该 CSS 类名,又该如何呢?在这种情况下,我可以为该 CSS 添加另一个属性并提供一个默认,可供 JSF 在未指定属性的时候使用。清单 14 给出了该属性:


清单 14. 重构后的  icon   组件
				
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
<html xmlns="http://www.w3.org/1999/xhtml"
    ...
    xmlns:composite="http://java.sun.com/jsf/composite">
    
    <composite:interface>
      ...
      <composite:attribute name="styleClass" default="icon" required="false"
/>
      ...
    </composite:interface>

    <composite:implementation>
      ...
      <h:graphicImage value="#{cc.attrs.image}"
                 styleClass="#{cc.attrs.styleClass}
"/>
      ...
    </composite:implementation>
</html>

  清单 14   中,我已经向这个图标组件的界面添加了一个属性,名为  styleClass ,并已经在此组件的实现中引用了该属性。有了这种更改,现在就可以为此图标的图像指定一个可选的 CSS 类,如下所示:

<util:icon actionMethod="#{places.logout}" 
                  image="#{resource['images:back-arrow.jpg']}"
            styleClass
="customIconClass"/>

如果不能指定  styleClass   属性,JSF 将使用默认值  icon

login   组件:一个完全可配置的组件

有了 JSF 2,就可以实现完全可配置的复合组件。例如,places 应用程序就包含了一个  login   组件,如图 7 所示:


图 7. places 应用程序的  login   组件
places 应用程序的 login 组件

清单 15 显示了这个 places 应用程序是如何使用  login   组件的:


清单 15. 使用  login   组件
				
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
  ...
  xmlns:comp="http://java.sun.com/jsf/composite/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   不仅参数化  login   组件的属性,比如名字和密码提示,它还将一个动作侦听器附加到了此组件的  Log In   按钮。该按钮由  login   组件的界面公开,如清单 16 所示:


清单 16.  login   组件
				
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:composite="http://java.sun.com/jsf/composite">

  <!-- INTERFACE -->
  <composite:interface>
    <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 class="prompt">
       #{cc.attrs.loginPrompt}
     </div>

     <panelGrid columns="2">
       #{cc.attrs.namePrompt}
       <h:inputText id="name" 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

  


  
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics