经典Java EE企业应用实战
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.7 使用UI标签创建视图页面

JSF提供了大量UI标签来简化应用的视图开发,这些UI标签本质上类似于ASP.NET的服务器组件——正如前面所介绍的,这些UI标签可以通过value、binding、action、actionListener等属性直接绑定到托管Bean的属性、托管Bean实例、托管Bean的方法等。

归纳起来,JSF应用的三大核心组件就是:

JSF容器管理的托管Bean。

系列UI标签组成的应用界面,应用界面的UI组件直接绑定到托管Bean。

导航规则。

2.7.1 UI标签概述

通常来说,JSF应用的页面总需要大量使用JSF标签。一个JSF页面大致包含以下元素:

两个标签库声明,用于声明两个JSF标签库。

一个<f:view…/>标签,JSF页面的所有UI标签必须位于该标签内。

一个<h:form…/>标签,JSF页面的表单UI标签都必须位于该标签内。

如果用户熟悉JSP自定义标签库的用法,那么使用JSF标签库应该难度不大,因为它们本质上也是JSP自定义标签。为了在JSP页面中使用自定义标签,通常在页面上使用taglib声明自定义标签库,JSF也为自定义标签提供了两个TLD(Tag Library Definition)文件,图2.4中显示了JSF中两个标签库的TLD文件。

JSF标签库与普通JSP标签库的最大区别在于:JSF的标签可以使用value、binding、action、actionListener直接绑定到服务器组件。

JSF提供了两组标签库:jsf_core.tld标签库包含一组核心标签,核心标签库中的标签用于执行与特定绘制无关的核心操作。表2.5列出了JSF中核心标签的分类。

表2.5 JSF核心标签

html_basic.tld标签库则定义了一组HTML组件标签,这些组件标签大都直接生成对应的HTML标签,因此这些标签用起来也非常简单。JSF提供的HTML标签如表2.6所示。

表2.6 JSF的HTML标签

上面的HTML标签在页面上生成对应的HTML标签都比较简单、直观,不过其中有两组标签容易混淆:

h:commandLink和h:outputLink:它们都会在HTML页面上生成HTML超链接,其中前者生成的超链接上还绑定了JavaScript代码,因此用户单击该超链接时会导致表单提交;后者只是生成一个普通超链接,因此用户单击该超链接只是简单跳转到其他页面。

h:dataTable和h:panelGrid:它们都会在HTML页面上生成HTML表格,其中前者生成的表格可以根据底层数据源(与JDBC数据源不同)动态地添加或删除行来显示数据;而后者则需要预先指定表格的行数。

从表2.6可以看出:JSF提供的用户界面组件既可以是简单的用户组件,例如,只是生成一个简单的单行文本框,也可以是复合组件,例如,<h:dataTable…/>可以生成一个复杂的表格。

JSF提供的组件体系非常灵活,这也是JSF的独到设计之一。JSF的组件体系大致包含如下几块:

一组以UIComponent为根父类的UI组件类,它们负责定义UI组件的状态和行为。

组件绘制器(Renderer),负责以不同的方式来表现组件。

事件和监听器模型,它们定义了如何处理组件事件。

转换器模型,它们定义了如何对组件的值进行数据类型转换。

校验器模型,它们定义了如何对组件的值进行用户输入校验。

JSF组件体系中的基础成员就是UI组件类和绘制器,其中UI组件类负责处理该组件的功能,例如保留组件状态,维护与托管Bean之间的绑定等;而绘制器则负责根据组件状态生成合适的HTML标签,并设置HTML标签状态。

JSF的UI组件是非常灵活的,它允许开发者自由扩展,因此组件编写者完全可以创建各种自定义组件。实际上,JSF所有用户组件类都是由UIComponentBase(它又是UIComponent的子类)派生而来,因此JSF所有用户组件都可从UIComponentBase继承到父类的默认状态和行为。

JSF用户组件除了会继承UIComponentBase基类之外,往往还会实现一个或多个接口,这些接口往往定义了一组组件需要满足的特定行为。JSF中的行为接口包含如下:

ActionSource:实现该接口的组件可以作为Action事件的事件源,可以触发Action事件。例如UICommand就实现了该接口,因此它的两个子类HtmlCommandButton、HtmlCommandLink都可以触发Action事件。

ActionSource2:它是ActionSource的扩展版,因此提供了与ActionSource类似的功能。不过它允许在引用处理方法时使用统一EL。

ValueHolder:实现该接口的组件负责维护一个值。

EditableValueHolder:它是ValueHolder的扩展版,它针对可编辑组件提供了更多额外的属性,例如处理输入校验等。

NamingContainer:实现该接口的组件要求它所包含的每个子组件都具有一个唯一ID。

StateHolder:实现该接口的组件必须在前后两次请求之后保持连续的状态。

对于大部分JSF应用的开发者而言,他们无须理解这些UI组件类和接口,只需直接使用JSF提供的UI标签即可。但对于组件开发者而言,他们就需要理解这些UI组件类和接口,这样才可以开发出满足应用具体要求的自定义组件。

图2.22显示了JSF UI组件类的继承关系图。

在JSF组件体系结构中,组件的功能由组件类定义,而组件的外观表现则由组件绘制器负责。这种逻辑和显示分离的设计与Swing组件设计如出一辙。这种设计至少具有如下优势:

组件类负责定义组件的功能、行为,而组件绘制器则负责组件外观。因此可以为一个组件类定义多个组件绘制器,从而允许让同一个组件呈现出多种不同的外观,以适应不同的客户端程序。

页面编写者、应用开发者可以根据需要自由选择组件和绘制器,从而“创造”出更适合应用的UI组件。

图2.22 JSF UI组件类的继承关系图

JSF RI提供的各种绘制器位于jsf-impl.jar压缩包中,

绘制工具箱(Render kit)就是整套绘制器的集合,它为它所支持的每个组件都提供一个或多个Renderer类,这样就允许将一个组件呈现出一种或多种外观形式。例如,UISelectMany组件具有3个不同的绘制器,其中一个绘制器将该组件绘制成一组复选框;另一个绘制器将该组件绘制成一个允许多选的列表框;第三个绘制器则将该组件绘制成一个允许多选的组合框。

JSF提供了标准的HTML绘制工具箱,在这种绘制工具箱中,每个UI组件对应一个JSF标签,每个标签由两个部分组成:

组件功能:由UIComponent的子类定义。

外观绘制:由Renderer类定义。

将表2.6和图2.22放在一起对比不难发现,HTML组件类和JSF标签之间的关系非常明显,只要将HTML组件类的Html前缀去掉,再将剩下部分的首字母小写,该类名就变成了相应的标签。

2.7.2 UI标签的通用属性

JSF UI标签大都会生成一个或多个HTML标签,因此使用这些UI标签时有一些属性是通用的,这些属性对大部分UI标签的作用都是相似的,下面列出UI标签的这些通用属性。

id:该属性为UI组件指定一个唯一标识。一般来说,UI组件并不需要指定id属性,JSF会为每个UI组件自动生成唯一标识。

immediate:该属性的属性值只能是true或false,用于指定是否立即处理UIInput组件和实现了ActionSource接口的UI组件上事件。如果将该UI组件上immediate设为true,JSF将在应用请求值阶段就处理该组件上的事件。

rendered:该属性指定一个条件表达式,只有当该条件为true时,该组件才会被绘制出来。

required:这是一个对输入组件才有效的属性,该属性的属性值只能是true或false,用于指定用户是否必须为该组件输入值。

requiredMessage:该属性与required属性结合使用。当为某个UI组件指定required为true时,如果用户没有为该组件输入值,则JSF将向用户提示requiredMessage指定的字符串。

value:该属性用于将组件的值绑定到托管Bean。

onblur:指定该属性相当于直接为HTML标签指定onblur属性。

onchange:指定该属性相当于直接为HTML标签指定onchange属性。

onclick:指定该属性相当于直接为HTML标签指定onclick属性。

ondblclick:指定该属性相当于直接为HTML标签指定ondblclick属性。

onfocus:指定该属性相当于直接为HTML标签指定onfocus属性。

onkeydown:指定该属性相当于直接为HTML标签指定onkeydown属性。

onkeypress:指定该属性相当于直接为HTML标签指定onkeypress属性。

onkeyup:指定该属性相当于直接为HTML标签指定onkeyup属性。

onmousedown:指定该属性相当于直接为HTML标签指定onmousedown属性。

onmousemove:指定该属性相当于直接为HTML标签指定onmousemove属性。

onmouseout:指定该属性相当于直接为HTML标签指定onmouseout属性。

onmouseover:指定该属性相当于直接为HTML标签指定onmouseover属性。

onmouseup:指定该属性相当于直接为HTML标签指定onmouseup属性。

style:指定该属性相当于直接为HTML标签指定style属性。

styleClass:指定该属性相当于直接为HTML标签指定class属性。

binding:该属性用于将组件本身绑定到托管Bean。

JSF要求所有的组件标签都放在<f:view…/>之内,该标签可以指定如下几个属性:

locale:指定一个国家语言的Locale,JSF应用会根据该Locale对该页面执行本地化。

renderKitId:该属性值指定一个renderKit的唯一标识符,JSF将根据该属性值选择对应的绘制器工具箱来绘制该页面。

beforePhase:该属性值是一个方法表达式,用于绑定生命周期监听器。该属性指定的方法必须有public void beforePhase(javax.faces.event.PhaseEvent)形式的方法签名。JSF会在每个生命周期阶段(除了恢复视图)之前调用该方法。

afterPhase:该属性值是一个方法表达式,用于绑定生命周期监听器。该属性指定的方法必须有public void beforePhase(javax.faces.event.PhaseEvent)形式的方法签名。JSF会在每个生命周期阶段(除了恢复视图)之后调用该方法。

2.7.3 表单相关标签

表单相关标签是JSF应用中最常用的UI标签,JSF应用使用JSF表单相关标签来定义输入页面,用于收集用户输入的信息。

1.使用<h:form…/>标签

表单相关标签中最常用的标签是<h:form…/>,它负责生成HTML表单标签,所有表单相关标签都应该放在该标签内,该标签除了可以接受前面介绍的通用属性之外,还可以接受如下属性:

prependId:该属性值只能是true或false,它指定是否将该表单的ID添加在其后代元素的ID之前。例如,我们为<h:form id="crazyit">指定了prependId="true",假如该表单包含<h:inputText id="abc"/>单行文本框,则JSF将会自动把该文本框的ID改变为crazyit_abc。

enctype:该属性指定该表单的编码类型。指定该属性相当于为HTML的<form…/>标签指定enctype属性。

target:该属性指定一个frame的名称,该属性指定frame将会负责显示该表单提交后所生成的响应。

使用<h:form…/>标签的示例代码如下:

      <h:form enctype="application/x-www-form-urlencoded">
          此处放置其他JSF标签
      </h:form>

需要指出的是,JSF提供的<h:form…/>标签不像Struts 2所提供的<form…/>标签,JSF提供的<h:form…/>标签没有提供页面布局功能,因此开发者可能还需要为之提供页面布局标签。

2.基本输入标签

在图2.22中看到UIInput组件下有4个子类:

HtmlInputText:对应于<h:inputText…/>标签,它在页面上生成一个单行文本输入框。

HtmlInputHidden:对应于<h:inputHidden…/>标签,它在页面上生成一个隐藏框。

HtmlInputSecret:对应于<h:inputSecret…/>标签,它在页面上生成一个密码输入框。

HtmlInputTextarea:对应于<h:inputTextarea…/>标签,它在页面上生成一个多行文本域。使用该标签可以额外指定rows、cols两个属性,这两个属性用于指定这个多行文本域的高度和宽度。

其中<h:inputText…/>和<h:inputSecret…/>可以指定一个size属性,该属性用于指定该输入框的最大长度。这3个标签的用法比较简单,下面给出简单的示例。

程序清单:codes\02\2.7\formTag\input.jsp

      <h1>表单标签</h1>
      <h:form>
      单行文本框:<h:inputText value="#{user.name}"/><br/>
      密码框:<h:inputSecret value="#{user.pass}" /><br/>
      多行文本域:<h:inputTextarea rows="4" cols="40"/><br/>
      隐藏域:<h:inputHidden value="#{user.grade}" /><br/>
      </h:form>

在浏览器中访问该页面看到如图2.23所示的效果。

图2.23 表单标签示例效果

3.UISelectMany对应的组件

UISelectMany派生了3个子类:

HtmlSelectManyCheckbox:生成一组允许多选的复选框。

HtmlSelectManyListbox:生成一个允许多选的列表框。

HtmlSelectManyMenu:生成一个允许多选的复合框。

使用这3个标签时必须与<f:selectItem…/>或<f:selectItems…/>标签结合使用,其中<f:selectItem…/>用于生成一个复选框或列表项——具体取决于它位于哪个标签之内,当它处于<h:selectManyCheckbox…/>之内时,它就会生成一个复选框;当它处于<h:selectManyListbox…/>、<h:selectManyMenu…/>之内时,它就会生成一个列表项。

<f:selectItem…/>可以额外指定如下3个属性:

itemLabel:该属性指定它所生成列表项或复选框的可视化标签值。

itemValue:该属性指定它所生成列表项或复选框的值。

value:<f:selectItem…/>与其他UI组件的value属性不同,它不是用于将该列表项、复选框的值绑定到托管Bean,而是用于将该组件本身绑定到托管Bean。

<f:selectItems…/>则可以一次性生成多个列表项或复选框,使用该标签时可以额外指定如下属性:

value:该属性值必须是一个List或数组,而且List或数组元素必须是SelectItem。

下面的简单页面示范了UISelectMany组件的用法。

程序清单:codes\02\2.7\formTag\selectMany.jsp

      <h1>表单标签</h1>
      <h:form>
      HtmlSelectManyCheckbox:
      <h:selectManyCheckbox value="#{user.name}">
          <f:selectItem itemLabel="疯狂Java讲义" itemValue="java"/>
          <f:selectItem itemLabel="疯狂Ajax讲义" itemValue="ajax"/>
          <f:selectItem itemLabel="疯狂XML讲义" itemValue="xml"/>
      </h:selectManyCheckbox>
      <br/>
      HtmlSelectManyListbox:
      <h:selectManyListbox value="#{user.pass}" size="5">
          <f:selectItem itemLabel="疯狂Java讲义" itemValue="java"/>
          <f:selectItem itemLabel="疯狂Ajax讲义" itemValue="ajax"/>
          <f:selectItem itemLabel="疯狂XML讲义" itemValue="xml"/>
      </h:selectManyListbox>
      <br/>
      HtmlSelectManyMenu:
      <h:selectManyMenu value="#{user.grade}" >
          <f:selectItem itemLabel="疯狂Java讲义" itemValue="1"/>
          <f:selectItem itemLabel="疯狂Ajax讲义" itemValue="2"/>
          <f:selectItem itemLabel="疯狂XML讲义" itemValue="3"/>
      </h:selectManyMenu>
      <br/>
      </h:form>

在浏览器中访问该页面看到如图2.24所示的效果。

注意

Firefox浏览器对<h:selectManyMenu…/>标签的支持并不好,避免在实际应用中使用这个标签。

4.UISelectOne对应的组件

UISelectOne派生了3个子类:

HtmlSelectOneRadio:生成一组单选按钮。

HtmlSelectOneListbox:生成一个只允许单选的列表框。

HtmlSelectOneMenu:生成一个只允许单选的下拉菜单。

图2.24 UISelectMany对应的组件

这3个标签和前面介绍的3个标签的功能基本相似,区别只是这里介绍的3个标签只能单选。下面的简单页面示范了这3个标签的用法。

程序清单:codes\02\2.7\formTag\selectOne.jsp

      <h1>表单标签</h1>
      <h:form>
      HtmlSelectOneRadio:
      <h:selectOneRadio value="#{user.name}">
          <f:selectItem itemLabel="疯狂Java讲义" itemValue="java"/>
          <f:selectItem itemLabel="疯狂Ajax讲义" itemValue="ajax"/>
          <f:selectItem itemLabel="疯狂XML讲义" itemValue="xml"/>
      </h:selectOneRadio>
      <br/>
      HtmlSelectOneListbox:
      <h:selectOneListbox value="#{user.pass}" size="5">
          <f:selectItem itemLabel="疯狂Java讲义" itemValue="java"/>
          <f:selectItem itemLabel="疯狂Ajax讲义" itemValue="ajax"/>
          <f:selectItem itemLabel="疯狂XML讲义" itemValue="xml"/>
      </h:selectOneListbox>
      <br/>
      HtmlSelectOneMenu:
      <h:selectOneMenu value="#{user.grade}" >
          <f:selectItem itemLabel="疯狂Java讲义" itemValue="1"/>
          <f:selectItem itemLabel="疯狂Ajax讲义" itemValue="2"/>
          <f:selectItem itemLabel="疯狂XML讲义" itemValue="3"/>
      </h:selectOneMenu>
      <br/>
      </h:form>

在浏览器中浏览该页面,看到如图2.25所示的效果。

图2.25 UISelectOne对应的组件

5.UISelectBoolean对应的组件

UISelectBoolean只有一个对应的标签:<h: selectBooleanCheckbox…/>,这个标签用于在页面上生成一个复选框,用户可以勾选或取消勾选该复选框。

<h: selectBooleanCheckbox…/>与前面的<h:selectManyCheckbox…/>、<h:selectOneCheckbox…/>标签不同,它的value属性必须绑定到boolean(或Boolean)类型的属性;而且它不需要与<f:selectItem…/>、<f:selectItems…/>元素结合使用。

6.UICommand对应的组件

UICommand组件对应于两个标签:

<h:commandButton…/>:该标签生成一个可以提交表单的按钮。

<h:commandLink…/>:该标签生成一个可以提交表单的超链接。

由于UICommand组件实现了ActionSource,因此它们都多了两个额外属性:

action:该属性值必须是一个方法表达式,且该方法的方法签名必须是public String xxx()形式。当该按钮、超链接被单击时,该属性指定的方法将被调用。

actionListener:该属性值必须是一个方法表达式,且该方法的方法签名必须是public void xxx(ActionEvent ae)形式。当该按钮、超链接被单击时,该属性指定的方法将被调用。

<h:commandButton…/>标签除了可以生成普通的提交按钮之外,还可以生成图片按钮。

<h:commandLink…/>标签用于生成<a…/>超链接,它不仅可以生成普通的文字链接,还可以生成图片链接。如果只希望生成普通的文字链接,为该标签指定value属性即可;如果希望生成图片链接,则在该标签内部嵌套HTML的<img…/>标签即可。

如下代码示范了UICommand对应组件的用法。

程序清单:codes\02\2.7\formTag\command.jsp

      <h1>表单标签</h1>
      <h:form>
      按钮:<h:commandButton value="单击我"/><br/>
      图片按钮:<h:commandButton
          image="http://www.crazyit.org/logo.jpg"/><br/>
      超链接:<h:commandLink value="超链接"/><br/>
      图片链接:<h:commandLink shape="circle" coords="30,30,5">
      <img src="http://www.crazyit.org/logo.jpg"/>
      </h:commandLink>
      </h:form>

在浏览器中浏览该页面,看到如图2.26所示的效果。

图2.26 UICommand对应的组件

2.7.4 其他标签

除了上面介绍的各种表单相关标签之外,JSF还提供了一系列其他标签,这些标签可以负责普通输出、数据显示和页面布局等功能,下面看看这些普通标签。

1.UIOutput对应的输出组件

UIOutput组件派生了4个子类,它们对应于如下4个标签:

<h:outputText…/>:该标签用于在页面上输出一段普通文本。该标签的value属性既可以是某个Bean的属性,也可以是国际化消息的key。

<h:outputLabel…/>:该标签用于在页面上生成一个HTML的<label…/>标签,因此可以为该标签指定一个for属性,该for属性相当于为HTML的<label…/>标签指定for属性。

<h:outputLink…/>:该标签用于在页面上生成一个HTML的普通超链接,它既可以创建一个普通的文本链接,也可以创建一个图片链接,这取决于<h:outputLink…/>元素之内的内容。该元素可以指定额外的target属性,该属性指定在哪个frame中打开超链接对应的页面。

<h:outputFormat…/>:该标签用于在页面上输出国际化消息,这一点与<h:outputText…/>的功能基本相似,但它具有MessageFormat.format()的功能。也就是说,该标签可以为带有占位符的消息填入参数。如果该标签的value属性对应一条带有占位符的国际化消息,则可以为该元素配置一个或多个<f:param…/>子元素,该子元素配置的参数将会用于填充国际化消息的占位符。

下面简单的页面代码示范了上面4个标签的用法。

程序清单:codes\02\2.7\otherTag\output.jsp

      <h1>其他标签</h1>
      输出国际化资源:<h:outputText value="#{mess.name}"/><br/>
      输出Bean属性:<h:outputText value="#{user.name}" /><br/>
      生成label标签:<h:outputLabel value="#{user.name}" /><br/>
      生成普通超链接:<h:outputLink value="http://www.crazyit.org">
      文字链接</h:outputLink><br/>
      生成普通超链接:<h:outputLink value="http://www.crazyit.org">
      <img src="http://www.crazyit.org/logo.jpg"></h:outputLink><br/>
      输出带占位符的国际化消息:<h:outputFormat value="#{mess.hello}">
      <f:param value="孙悟空"/>
      <f:param value="www.crazyit.org"/>
      </h:outputFormat>

上面页面用到了国际化消息资源,因此这个示例应用采用了配置文件来加载国际化消息资源。下面是faces-config.xml文件中关于加载国际化消息资源的配置片段。

      <application>
          <resource-bundle>
              <base-name>org.crazyit.jsf.message</base-name>
              <var>mess</var>
          </resource-bundle>
      </application>

上面配置片段指定该应用中国际化消息资源文件的basename为message,且应该位于org.crazyit.jsf路径下。下面是本应用中国际化消息资源文件的代码。

      name=孙悟空
      hello={0},您好!,欢迎光临{1}。

注意

上面国际化消息资源文件的消息资源包含了非西欧字符,因此不要忘记使用native2ascii命令来处理这个国际化消息资源文件。

上面国际化消息资源中第二条消息中包含两个占位符,为了输出该国际化消息时为该占位符填充参数,上面程序中粗体字代码使用了两个<f:param…/>元素,它们就可以为该国际化消息中占位符填入参数。

在浏览器中浏览该页面,将可以看到如图2.27所示的效果。

图2.27 使用UIOutput组件

2.UIPanel对应的布局组件

JSF提供了两个布局相关的标签:

<h:panelGrid…/>:该标签用于生成一个HTML表格。使用该标签时可以额外指定如下几个属性:

· bgcolor:该属性指定该标签生成表格的背景色,也就是相当于为该标签生成的HTML<table…/>标签指定bgcolor属性。

· border:该属性指定该标签生成表格的边框宽度,也就是相当于为该标签生成的HTML<table…/>标签指定border属性。

· captionClass:该属性指定该标签生成表格内<caption…/>元素的class属性值,也就是相当于为该标签生成的HTML <table…/>标签内的<caption…/>子标签指定class属性。

· captionStyle:该属性指定该标签生成表格内<caption…/>元素的style属性值,也就是相当于为该标签生成的HTML <table…/>标签内的<caption…/>子标签指定style属性。

· cellpadding:该属性指定该标签生成表格的单元格填充宽度,也就是相当于为该标签生成的HTML <table…/>标签指定cellpadding属性。

· cellspacing:该属性指定该标签生成表格的单元格空白大小,也就是相当于为该标签生成的HTML <table…/>标签指定cellspacing属性。

· columnClasses:该属性指定该标签生成表格内各列的CSS样式,也就是相当于为该标签生成的HTML <table…/>标签内的<td…/>子标签指定class属性。该属性可以指定多个以逗号隔开的CSS样式值,此处指定的CSS样式数量应与columns指定的列数量相等,如果此处指定的CSS样式值数量小于columns指定的列数量,则后面几列的<td…/>子元素将不会添加class属性;如果此处指定的CSS样式值数量大于columns指定的列数量,则多出的CSS样式值将被直接丢弃。

· columns:指定列数量。从这里可以看出,当使用<h:panelGrid…/>定义表格时只指定了该表格有多少列,并没有指定有多少行。这个表格可以包含多少行是由JSF通过计算来决定的——假如我们指定该表格包含3列,如果在标签所生成的表格内放置了7个元素,那么JSF将自动计算出该表格包含3行。

· footerClass:该属性指定该标签生成表格内<footer…/>元素的class属性值,也就是相当于为该标签生成的HTML <table…/>标签内的<footer…/>子标签指定class属性。

· headerClass:该属性指定该标签生成表格内<header…/>元素的class属性值,也就是相当于为该标签生成的HTML <table…/>标签内的<header…/>子标签指定class属性。

· rowClasses:该属性指定该标签生成表格内各行的CSS样式,也就是相当于为该标签生成的HTML <table…/>标签内的<tr…/>子标签指定class属性。该属性可以指定多个以逗号隔开的CSS样式值,这些属性值将采用循环的方式自动应用到该表格的每行。假如此处指定了两个CSS样式值,则该表格内1、3、5…等行将应用第一个CSS样式值,2、4、6…等行将应用第二个CSS样式值。

· width:指定生成表格宽度。

<h:panelGroup…/>:该标签用于将多个元素组合成一个元素。假如页面中定义了columns="3"的表格,如果在<h:panelGrid…/>元素内定义了3个元素,则它们将分别占据3个单元格。如果使用<h:panelGroup…/>将这3个元素组合成一个元素,则它们只占据一个单元格。使用该标签可以指定一个额外的属性:

· layout:如果该属性的值为block,该元素将生成一个HTML的<div…/>元素;否则该元素生成一个HTML的<span…/>元素。

下面的页面代码示范了使用了JSF的布局标签。

程序清单:codes\02\2.7\otherTag\panel.jsp

      <h1>其他标签</h1>
      <h:panelGrid columns="3" width="500px" border="1">
          <!-- 使用facet标签生成caption -->
          <f:facet name="caption">
              <h:outputText value="表格标题"/>
          </f:facet>
          <!-- 指定生成表格头 -->
          <f:facet name="header">
              <h:outputText value="表格头"/>
          </f:facet>
          <h:outputText value="第1个单元格"/>
          <h:outputText value="第2个单元格"/>
          <h:outputText value="第3个单元格"/>
          <h:outputText value="第4个单元格"/>
          <h:outputText value="第5个单元格"/>
          <h:outputText value="第6个单元格"/>
          <h:outputText value="第7个单元格"/>
          <!-- 将两个输出元素组合成一个元素 -->
          <h:panelGroup layout="block"
              style="background-color:#dddddd">
          <h:outputText value="第8个单元格"/>
          <h:outputText value="第9个单元格"/>
          </h:panelGroup>
          <h:outputText value="第10个单元格"/>
      </h:panelGrid>
      <!-- 将两个元素组合成一个元素 -->
      <h:panelGroup layout="block"
          style="background-color:#dddddd">
          <h:inputText/>
          <h:inputText/>
      </h:panelGroup>

上面页面使用<h:panelGrid…/>、<h:panelGroup…/>元素进行布局,其中使用<h:panelGrid…/>元素定义了宽度为500px且包含3列的表格。上面页面代码中的粗体字代码使用一个<h:panelGroup…/>元素将两个普通元素组合在一起,JSF会把这两个元素放入一个单元格中——其余每个元素占据一个单元格。在浏览器中浏览该页面将看到如图2.28所示的效果。

图2.28 使用UIPanel对应的布局组件

3.使用UIData显示数据

JSF提供了UIData组件来显示二维数据,UIData组件对应于<h:dataTable…/>标签,该标签将会在页面上生成一个HTML表格。UIData组件将会自动迭代数据源中每条记录,并为之创建一个表格行。

与UIData组件结合使用的是UIColumn组件,UIColumn组件对应于<h:column…/>标签,该标签将会在<h:dataTable…/>生成的表格内生成一个表格列。

<h:dataTable…/>标签的value属性用于指定需要在表格中显示的数据,这些数据可以是如下形式的数据:

JavaBean实例的集合或数组(这是实际应用中最常见的情形)。

单个Bean。

javax.faces.model.DataModel对象。

java.sql.ResultSet对象。

javax.servlet.jsp.jstl.sql.ResultSet对象。

javax.sql.RowSet对象。

由于<h:dataTable…/>元素也会输出一个HTML表格,因此使用<h:dataTable…/>元素时也可指定与<h:panelGrid…/>标签相同的属性。除此之外,该标签可以指定如下额外的属性:

first:该属性指定<h:dataTable…/>从数据源的第几条记录开始显示;如果该属性指定为0,则表明<h:dataTable…/>从数据源的第一条记录开始显示。这是一个控制分页的属性。

rows:该属性指定<h:dataTable…/>总共显示数据源的多少条记录;如果该属性指定为0,则表明<h:dataTable…/>将显示数据源的全部记录。这是一个控制分页的属性。

var:该属性指定一个变量名,该<h:dataTable…/>元素内部即可通过该变量访问当前正在迭代输出的数据行。

下面的页面代码示范了如何使用<h:dataTable…/>、<h:column…/>标签来迭代显示数据源中的数据。

程序清单:codes\02\2.7\otherTag\data.jsp

      <h1>其他标签</h1>
      <h:dataTable width="600px" border="1"
          value="#{viewBook.books}" var="book"
          rowClasses="odd,even">
          <!-- 使用facet标签生成caption -->
          <f:facet name="caption">
              <h:outputText value="图书列表"/>
          </f:facet>
          <!-- 定义第一列 -->
          <h:column>
              <f:facet name="header">
                  <h:outputText value="图书ID"/>
              </f:facet>
              <h:inputText value="#{book.id}" size="3"/>
          </h:column>
          <!-- 定义第二列 -->
          <h:column>
              <f:facet name="header">
                  <h:outputText value="图书书名"/>
              </f:facet>
              <h:outputText value="#{book.name}"/>
          </h:column>
          <!-- 定义第三列 -->
          <h:column>
              <f:facet name="header">
                  <h:outputText value="技术支持站点"/>
              </f:facet>
              <h:outputLink value="http://www.#{book.website}">
                  <h:outputText value="疯狂Java联盟"/>
              </h:outputLink>
          </h:column>
          <f:facet name="footer">
              <h:panelGroup>
                  <h:outputText value="总数:5本图书"/>,
                  <h:outputText value="总价:268.00元"/>
              </h:panelGroup>
          </f:facet>
      </h:dataTable>

上面页面代码中使用<h:dataTable…/>组件来显示viewBook托管Bean中books属性,在该标签内定义了3个<h:column…/>子元素,每个<h:column…/>子元素代表一列。上面程序中第一段粗体字代码为该表定义了第一列,并使用<f:facet…/>定义了一个列标题。

上面页面代码中的第二段粗体字代码使用<facet…/>定义了汇总信息,定义汇总信息时使用了<f:facet…/>标签,由于<f:facet…/>标签只能包含一个子项,因此我们使用<h:panelGroup…/>对多个组件进行了分组。

上面的<h:dataTable…/>标签用于显示viewBook托管Bean中的books属性,下面是该viewBook托管Bean的源代码。

程序清单:codes\02\2.7\otherTag\WEB-INF\src\org\crazyit\jsf\ViewBookBean.java

      public class ViewBookBean
      {
          public List<BookBean> getBooks()
          {
              //添加5本图书
              List<BookBean> books =
                  new ArrayList<BookBean>();
              books.add(new BookBean(1 , "疯狂Java讲义"
                  , "crazyit.org"));
              books.add(new BookBean(2 , "轻量级Java EE企业应用实战"
                  , "crazyit.org"));
              books.add(new BookBean(3 , "疯狂Ajax讲义"
                  , "crazyit.org"));
              books.add(new BookBean(4 , "疯狂XML讲义"
                  , "crazyit.org"));
              books.add(new BookBean(5 , "经典Java EE企业应用实战"
                  , "crazyit.org"));
              return books;
          }
      }

从上面程序可以看出,viewBook托管Bean的books属性(由getBooks()方法体现)将返回一个包含5个BookBean的List集合。使用浏览器来浏览上面页面,将看到如图2.29所示的效果。

图2.29 使用UIData显示数据

如果需要显示的数据过多,那就需要对数据进行分页,对数据表进行分页只要为<h:dataTable…/>标签指定first、rows两个属性即可。如果为上面页面中的<h:dataTable…/>标签指定first="2" rows="2",将可以看到如图2.30所示的显示效果。

图2.30 使用UIData显示数据(分页显示效果)

4.UIGraphic对应的图像组件

JSF提供了UIGraphic组件用于显示图片,该组件对应于<h:graphicImage…/>标签,该标签将在页面上输出一个HTML的<img…/>标签。

<h:graphicImage…/>标签用起来比较简单,使用该标签可以额外指定如下几个属性:

url:该属性是value属性的别名。

value:该属性值将输出成<img…/>标签的src属性值。

height:该属性指定生成图片的高度。

width:该属性指定生成图片的宽度。

下面代码就使用<h:graphicImage…/>标签来显示图片。

      <h:graphicImage value="http://www.crazyit.org/logo.jpg" alt="疯狂Java联盟"/>

上面的<h:graphicImage…/>标签将生成HTML的<img src="http://www.crazyit.org/logo.jpg" alt="疯狂Java联盟" />标签。

JSF提供的HTML标签库中除了这里介绍的这些标签之外,还有<h:message…/>标签和<h:messages…/>标签,其中<h:message…/>标签用于显示与指定UI标签相关的错误消息;<h:messages…/>用于显示指定页面的全部错误消息。本书下一章有类型转换、输入校验的章节将会有这两个标签的更详细介绍和示例。