这里展示一个JSF2.0组件是如何开发的。示例程序是一个简单的数字文本框,在使用时前端页面会自动加载css和js,对文本框进行功能增强和美化,后台java类控制文本框的值。
项目环境:
1、JSF2.1+
2、JDK1.6+
3、Tomcat6.0+
4、Eclipse3.6+ 我用的Indigo
目录结构:
1、src下建立META-INF目录,放置资源文件和配置文件,这样可以把class文件、配置文件、资源文件js和css打包成独立的jar包,在多个项目间复用。当项目中使用这个组件时,JSF2.0框架会根据jar中的xml配置,找到组件的class,根据class内的annotation自动引入并读取所需的js和css资源文件。
2、组件通常由一个组件类和一个组件的渲染类组合完成。组件类提供给业务开发者使用,组件渲染类提供给JSF框架用于处理组件的解析和渲染。
NumberInputText.java
package test.component; import javax.faces.component.UIComponentBase; //组件通常继承UIComponentBase public class NumberInputText extends UIComponentBase { //用于xml配置和render public static final String COMPONENT_FAMILY = "test.NumberInputText"; public static final String COMPONENT_TYPE = "test.NumberInputText"; enum PropertyKeys { //组件的属性 value } @Override public String getFamily() { return COMPONENT_FAMILY; } public Integer getValue() { //通常用getStateHelper()来存放属性值,stateHelper可以自动处理属性前后台的序列化和反序列化 return (Integer) getStateHelper().get(PropertyKeys.value); } public void setValue(Integer value) { getStateHelper().put(PropertyKeys.value, value); } }
package test.component; import java.io.IOException; import java.util.Map; import javax.faces.application.ResourceDependencies; import javax.faces.application.ResourceDependency; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import javax.faces.render.FacesRenderer; import javax.faces.render.Renderer; //告诉这个类是渲染哪个组件的,通过componentFamily和rendererType到配置文件中定位组件 @FacesRenderer(componentFamily = NumberInputText.COMPONENT_FAMILY, rendererType = NumberInputText.COMPONENT_TYPE) //组件依赖的资源文件,必须位于classes/META-INF/resources目录下,或WebContent/resources目录下,在页面上渲染组件时会自动引入资源文件 @ResourceDependencies({ @ResourceDependency(library = "test/css", name = "numberInputText.css", target = "head"), @ResourceDependency(library = "test/js", name = "numberInputText.js", target = "head")}) //渲染类必须继承Renderer public class NumberInputTextRenderer extends Renderer { //渲染函数。在前端页面上输入组件的HTML代码 @Override public void encodeBegin(FacesContext context, UIComponent component) throws IOException { ResponseWriter writer = context.getResponseWriter(); //组件的ID。如果前端页面没有给组件定义ID,JSF框架会自动给组件分配一个ID String clientId = component.getClientId(context); NumberInputText numberInputText = (NumberInputText) component; //输入一个<input id=[clientId] name=[clientId] class="numberInputText" value=[value] />的HTML元素 writer.startElement("input", component); writer.writeAttribute("id", clientId, null); writer.writeAttribute("name", clientId, null); writer.writeAttribute("class", "numberInputText", null); writer.writeAttribute("value", numberInputText.getValue(), null); writer.endElement("input"); //输入一段javascript脚本,增强input的功能 writer.startElement("script", component); writer.write("createNumberInputText(document.getElementById('" + clientId + "'));"); writer.endElement("script"); } //解析函数。解析从提交请求中获取的数据,设置组件的属性 @Override public void decode(FacesContext context, UIComponent component) { String clientId = component.getClientId(context); NumberInputText numberInputText = (NumberInputText) component; //获取请求参数 Map<String, String> parameterMap = context.getExternalContext().getRequestParameterMap(); String value = parameterMap.get(clientId); try { //设置组件值 numberInputText.setValue(Integer.parseInt(value)); } catch (NumberFormatException e) { //e.printStackTrace(); } } }
function createNumberInputText(element) { element.onkeyup=function(e) { //如果input.value有非数字字符,改变input的样式 if (isNaN(element.value)) { element.className = "numberInputTextError"; } else { element.className = "numberInputText"; } }; }
.numberInputText {/*正常时的样式*/ border:solid 1px #759dc0; } .numberInputTextError {/*有非数字字符时使用此样式*/ border:solid 1px #d46464; background-color: #e5f2fe; color:red; }
<?xml version="1.0" encoding="UTF-8"?> <faces-config xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_1.xsd" version="2.1"> <component> <component-type>test.NumberInputText</component-type> <component-class>test.component.NumberInputText</component-class> </component> </faces-config>
<?xml version="1.0" encoding="UTF-8"?> <facelet-taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"> <namespace>http://test.component</namespace> <tag> <tag-name>numberInputText</tag-name> <component> <component-type>test.NumberInputText</component-type> <renderer-type>test.NumberInputText</renderer-type> </component> </tag> </facelet-taglib>
import javax.faces.bean.ManagedBean; import test.component.NumberInputText; @ManagedBean public class Test { private NumberInputText text1; private NumberInputText text2; public void print() { //获取text1的值,赋值给text2 text2.setValue(text1.getValue()); } public NumberInputText getText1() { return text1; } public void setText1(NumberInputText text1) { this.text1 = text1; } public NumberInputText getText2() { return text2; } public void setText2(NumberInputText text2) { this.text2 = text2; } }
<!DOCTYPE HTML> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:t="http://test.component"><!-- xmlns:t="http://test.component"引入自定义组件 --> <h:head><!-- 必须用<h:head>,组件才能自动引入依赖的资源文件 --> <meta charset="utf-8" /> <title>Number Input Test</title> </h:head> <body> <h:form><!-- 必须有<h:form>,后台才能正常运行组件功能 --> <t:numberInputText binding="#{test.text1}" /> <h:commandButton value="确定" actionListener="#{test.print}" /> <t:numberInputText binding="#{test.text2}" /> </h:form> </body> </html>
url:http://localhost:8080/JsfComponent/test.faces
有非数字字符时,样式有变化
这个组件因为只继承了UIComponentBase,因此不支持ajax特性,也不支持listener事件。如果要支持ajax或者listener事件,需要组件类继承UIInput。这里只是为了演示组件是如何开发的,在实际应用中,我们一般对于数据交互组件,如输入框、日期控件、下拉框等,都直接继承UIInput,对于一般的显示组件,如Layout、Tree等才继承UIComponentBase。
下载示例代码
JSF2.0官方实现Mojarra提供了基于html标准控件实现的组件库,这套组件库在前端页面表现太弱,开发企业级软件项目不实用。通常企业要根据自己的实际情况,结合一套丰富的javascript ui库,如ExtJs、JQuery ui等来打造自己的JSF组件库。后面我将一步步演示如何利用一套完整的javascript ui库来打造企业组JSF组件库。