如果只用 Facelets 定义和使用模板,那么可能会有点大材小用了。虽然 Facelets 的模板化特性完整而且丰富,但并不是 Facelets 真正出色的地方。Facelets 把它的精华放在复合构件上,下面我们就该方面的内容进行详细的介绍。
复合构件,正如其名字所示,让您能够从现有构件组装和定制一个新构件,能重复发挥出OperaMasks构件模型的优势。复合构件具有如下的特点:
少量的配置;
无需 Java 代码;
开发人员可以方便向其附加功能;
修改后执行热部署;
下面我们将重点放在创建和使用复合构件的步骤上。但在开始之前,先要确保能够清楚地理解是什么让这些方便的小代码段这么棒。首先我们来看看下面这段代码:
....
<h:column>
<f:facet name="header">
<h:panelGroup>
<h:outputText value="Title" />
<f:verbatim>[</f:verbatim>
<h:commandLink styleClass="smallLink" action="#{CDManagerBean.sort}">
<h:outputText id="ascTitle" value="asc" />
<f:param name="by" value="title" />
<f:param name="order" value="asc" />
</h:commandLink>
<h:outputText value="," />
<!-- Sort descending -->
<h:commandLink styleClass="smallLink" action="#{CDManagerBean.sort}">
<h:outputText id="decTitle" value="dec" />
<f:param name="by" value="title" />
<f:param name="order" value="dec" />
</h:commandLink>
<f:verbatim>]</f:verbatim>
</h:panelGroup>
</f:facet>
<w:outputText value="#{cd.title}" />
</h:column>
<!-- Artist -->
<h:column>
<f:facet name="header">
<h:panelGroup>
<h:outputText value="Artist" />
<f:verbatim>[</f:verbatim>
<h:commandLink styleClass="smallLink" action="#{CDManagerBean.sort}">
<h:outputText id="ascArtist" value="asc" />
<f:param name="by" value="artist" />
<f:param name="order" value="asc" />
</h:commandLink>
<h:outputText value="," />
<!-- Sort descending -->
<h:commandLink styleClass="smallLink" action="#{CDManagerBean.sort}">
<h:outputText id="decArtist" value="dec" />
<f:param name="by" value="artist" />
<f:param name="order" value="dec" />
</h:commandLink>
<f:verbatim>]</f:verbatim>
</h:panelGroup>
</f:facet>
<h:outputText value="#{cd.artist}" />
</h:column>
....上面代码展示了某清单页面的局部内容,用于生成列标题和升序/降序排列链接。但我们可以看到,存在多个地方的重复代码。当我们的列数增加,会如何呢?需要更多、更多的工作。
下面我们看看使用复合构件后的代码:
<my:column entity="#{cd}" fieldName="title" backingBean="#{CDManagerBean}"/>
<my:column entity="#{cd}" fieldName="artist" backingBean="#{CDManagerBean}"/>似乎我们用2行代码就替代了 70 行或更多行代码!可以看出,my:column 即是一个复合构件。下面我们就来看看,如何定义这样一个复合构件。
以下是创建复合构件的步骤:
创建 复合构件标识文件;
在 web.xml 中声明该标示;
创建复合构件模板文件;
用命名空间导入标识文件;
复合构件的标识文件 是符合 facelet_taglib_1_0.dtd规范的文件。在概念上它与 JSP 中的 TLD 文件相似。下面是一个示例标示文件:
<!-- mycomponent.taglib.xml--> <!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "http://java.sun.com/dtd/web-facesconfig_1_0.dtd"> <facelet-taglib><namespace>http://www.mycompany.com/aom</namespace> <tag>
<tag-name>column</tag-name>
<source>column.xhtml</source> </tag> </facelet-taglib>
有了一个标记库是很好,但是要让它有用,还必须把它的存在告诉 Facelets我们新增加了一个构件。所以我们需要在 web.xml 文件中用 facelets.LIBRARIES 初始化参数做这件事,如下所示:
<!-- web.xml--> <context-param> <param-name>facelets.LIBRARIES</param-name> <param-value> /WEB-INF/facelets/tags/mycomponent.taglib.xml </param-value> </context-param>
模板文件是一个标准的Facelets页面,具体的标签使用方式请参看上节。代码如下所示:
<!--column.xhtml -->
....
<ui:composition>
<!-- The label attribute is optional. Generate it if it is missing. -->
<c:if test="${empty label}">
<c:set var="label" value="${fieldName}" />
</c:if>
<!-- The sort attribute is optional. Set it to true if it is missing. -->
<c:if test="${empty sort}">
<c:set var="sort" value="${true}" />
</c:if>
<h:column>
<f:facet name="header">
<h:panelGroup>
${label}
<c:if test="${sort}">
[
<h:commandLink styleClass="smallLink"
action="#{backingBean.sort}">
<h:outputText value="asc" />
<f:param name="by"
value="${fieldName}"/>
<f:param name="order" value="asc"/>
</h:commandLink>
,
<!-- Sort descending -->
<h:commandLink styleClass="smallLink"
action="#{backingBean.sort}">
<h:outputText value="asc" />
<f:param name="by"
value="${fieldName}"/>
<f:param name="order" value="dec"/>
</h:commandLink>
]
</c:if>
</h:panelGroup>
</f:facet>
<!-- Display the field name -->
<h:outputText value="#{entity[fieldName]}"/>
</h:column>
</ui:composition>
.....创建了标识文件并在 Facelets 标记库中定义了它之后,就可以使用它了。当Facelets页面需要使用该构件时,只需要在其命令空间内导入即可,如下所示:
<f:view xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:w="http://www.apusic.com/jsf/widget" xmlns:layout="http://www.apusic.com/jsf/layout"xmlns:my="http://www.mycompany.com/aom" xmlns:ui="http://java.sun.com/jsf/facelets" renderKitId="AJAX"> ....
<my:column entity="${cd}" fieldName="title" backingBean="#{CDManagerBean}"/> .....