3.3 复合组件
JSF使用Facelets的功能和资源的处理来定义复合组件,定义在Facelets标记文件中的一个或多个JSF组件组成了一个复合组件。这个.xhtml文件存储在资源库中,可以从页面的任意区域创建一个可重用的组件。
复合组件在定义页面中定义,在使用页面中使用。定义页面使用定义元数据(或参数),使用<cc:implementation>
定义实现,其中cc是http://xmlns.jcp.org/jsf/composite/
命名空间的前缀。JSF规范的未来版本可能会放宽对指定元数据的规定,因为它可以从实现本身派生。
可以使用JSF1.2定义一个复合组件,但这需要对JSF的生命周期有更深入的了解,并且需要编写多个文件。JSF2只用一个XHTML文件即可实现,简化了复合组件的定义。
例如,在一个Facelet中有如下代码片段来显示登录表单:
这段代码将呈现一个两行三列的表,如图3-1所示。
第一列显示要输入的字段的提示,第二列显示文本输入框,数据可以输入其中,第三列(它开始是空的)用于显示对应字段的消息。第一行的输入值绑定到User.name字段,第二行中的输入值绑定到User.password字段。还有一个命令按钮,单击该按钮将调用UserService的Bean的Register方法。
如果这个登录表单要被显示在多个页面,那么应当将这段代码片段转换成一个复合组件而不是到处复制这段代码。这需要将代码片段复制到一个.xhtml文件中,该文件位于标准资源目录的一个库中。基于约定优于配置的思想,这段代码片段会自动分配一个命名空间和一个标签名。
比如,前述的片段被复制到resources/mycomp目录下的login.xhtml文件中,其定义的页面所示如下:
在这段代码中,cc:interface用于定义描述组件特性的元数据,如支持的属性、facet和事件监听的连接点。cc:implementation包含代替复合组件的标记。
复合组件的命名空间是把http://xmlns.jcp.org/jsf/composite/和mycomp
连接起来。标签名是不带.xhtml后缀的页面文件名:
比方说,这段代码片段在不同页面中单击提交按钮时,需要传递不同的值表达式(而非#{ user.name }
)并调用不同的方法(而不是#{ userService.register}
)。定义的页面可以传递的值如下:
在这段代码中,所有的参数都显式地在cc:interface中被指定。第三个参数有一个目标属性指向ccForm:loginButton。
在cc:implementation中:
- h:form有一个id属性是必需的,以便在表单内的按钮可以明确地引用。
- h:inputText现在使用#{cc.attrs.xxx}代替#{ user.xxx }。#{ cc.attrs }是一个默认的EL表达式,对复合组件的作者可用,提供访问当前复合组件的属性。在本例中,#{ cc.attrs}有name和password属性。
- actionListener是一个事件监听器的连接点。它被定义为一种方法签名并描述了该方法签名。
- h:commandButton有一个id属性,以便它可以在h:form内被明确指定。
用户名、密码和actionListener传递使用页面中需要的属性:
现在,使用页面可以传递不同的backing bean,在点击提交按钮时,可以调用不同的业务方法。
总而言之,复合组件提供了以下好处。
- 遵循了不重复自己(DRY)的设计模式,代码一处保存,到处重用。
- 允许开发人员不使用任何Java代码或XML配置编写新的组件。