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…/>用于显示指定页面的全部错误消息。本书下一章有类型转换、输入校验的章节将会有这两个标签的更详细介绍和示例。