一个双联动JSF组件的制作过程 ,详细剖析
|
lanyin.cn
2008-03-14
以下是一个JSF组件所需的几个文件,涉及约5(包括js文件)个文件,主要文件两个(针对JSF1.2)
1.UIComponent 用于渲染组件自身,简单一点就是渲染html代码。(也可以使用Renderer类进行独立渲染) 2.UIComponentTag 组件的jsp标签处理程序类,主要用于指示组件在页面的显示及相关的属性等。 3.Javascript文件(可有可无) 如果你的组件要使用到较多的javascript,还是建议单独作为一个文件存在,可以使用一个servlet加载该js 4.tld文件(很简单) 这是一个标签库描述符文件,主要用于注册该组件所用到的属性,如:id,rendered,binding等 5.faces-config.xml(很简单)主要是用于注册你的组件 实际上制作一个JSF组件,基本上你只要处理好UIComponent及UIComponentTag即可,重点就是这两个,其它文件只是简单的收尾工作。 下面以我制作的一个实现双联动的选择框组件(Htmllinkage)为例进行说明,该组件在页面渲染了两个select,并可进行双联动选择,使用方法及效果在我的上一篇文章中 http://www.blogjava.net/huliqing/archive/2008/03/06/184315.html#184353 这里是介绍并分析隐藏在组件背后的默默无闻的代码,篇幅会较长,如果你对组件原理不感兴趣,那么你也可以只知道如何简单使用它就可以。看代码需要耐心,特别是看他人的代码,还要学会分析,分析代码给他人看也是一个锻炼逻辑思维能力的过程。 开始进入正题,下面是HtmlLinkage组件所需文件所对应的各个部分: 1.UIComponent -> biz.tbuy.share.components.linkage.HtmlLinkage; 2.UIComponentTag -> biz.tbuy.share.components.linkage.HtmlLinkageTag; 3.Javascript -> biz/tbuy/share/components/linkage/linkage.js; (js文件放在了同一个包中) 4.tld文件 -> Tcoco.tld 5.faces-config.xml -> faces-config.xml (最好以这一个名称命名,当你打为jar包后,JSF会自动加载jar内以faces-config.xml命名的文件) ================================================== 第一部分(UIComponent):HtmlLinkage package biz.tbuy.share.components.linkage; import biz.tbuy.share.components.TbuyExtensionsFilter; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.faces.component.UIInput; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; /** * * @author huliqing */ public class HtmlLinkage extends UIInput{ // 当前文件所在的包路径,及相关的资源文件,如图片,JS等。以便于加载这些资源 // ------------------------------------------------------------ encode // 渲染第一个选择框} ================================================== 第二部分(UIComponentTag):HtmlLinkageTag HtmlLinkageTag类主要用于指定并绑定组件的各个属性,并关联到组件的核心类,如:HtmlLinkage package biz.tbuy.share.components.linkage; import javax.el.ValueExpression; import javax.faces.component.UIComponent; import javax.faces.webapp.UIComponentELTag; /** * * @author huliqing */ public class HtmlLinkageTag extends UIComponentELTag{ // JSF1.2的时候建议扩展自UIComponentTag private ValueExpression param1; // 这里是组件在jsp页面中的三个自定义属性 } ================================================== 第三部分(Javascript):linkage.js 这里是我的组件所用到的javascript,如果你的组件没有用到javascript.就没有必要这个部分了, 另一个关于javascript及可能用到的图片资源的装载问题,我使用的是一个servlet进行处理: biz.tbuy.share.components.TbuyExtensionsFilter 类似myfaces的扩展过滤器,该类主要用将请求的js或图片资源处理并write到客户端,如果直接在组件 中渲染js会非常麻烦,也不容易修改。 var tbuy_linkage_clientId;// 组件id,为了不与其它组件的变量冲突,我都把变量名取得较长 var tbuy_linkage_selectA; // 第一个选择框 var tbuy_linkage_selectB; // 第二个选择框 var tbuy_linkage_params = new Array(); // 这里用于存放分析后的各个可选项 function tbuy_linkage_init(clientId) { // 初始化各个选项 if (tbuy_linkage_clientId == null) {}tbuy_linkage_clientId = clientId;} // 初始化A选择框的各选项 function tbuy_linkage_setSelectA() { for (var i = 0; i < tbuy_linkage_params.length; i++) {}var temp = tbuy_linkage_params[i];} // 选择selectA框后根据其值设置selectB的值 function tbuy_linkage_select(val) { for (var i = 0; i < tbuy_linkage_params.length; i++) {}var selA = tbuy_linkage_params[i];} // 该方法用于清除selectA的所有选项,用于重设A框时所用 function tbuy_linkage_clearSelectA() { while (tbuy_linkage_selectA.hasChildNodes()) {}tbuy_linkage_selectA.removeChild(tbuy_linkage_selectA.firstChild);} // 同上 function tbuy_linkage_clearSelectB() { while (tbuy_linkage_selectB.hasChildNodes()) {}tbuy_linkage_selectB.removeChild(tbuy_linkage_selectB.firstChild);} // 设置selectB的各个选项 function tbuy_linkage_setSelectB(objArr) { for (var i = 0; i < objArr.length; i++) {}var obj = objArr[i];} // 该方法用于分析隐藏域中的值,即所有可选类型的字符串形式.字符串规律像是这样(可参照核心类HtmlLinkage中隐藏域的渲染): //aa,aa==aa_1,aa_1;aa_2,aa_2;aa_3,aa_3;@@bb,bb==bb_1,bb_1;bb_2,bb_2;bb_3,bb_3;@@cc,cc==cc_1,cc_1;cc_2,cc_2;cc_3,cc_3;@@ function tbuy_linkage_parse(str) { var strArr = new Array();} // 配合上面的分析 function tbuy_linkage_getAsArray(valueStr) { var objArr = new Array();} ================================================== 第四部分(tld文件):Tcoco.tld 为了让组件能在jsp页面中使用,还需要一个tld标签描述符,下面是我的组件的标签描述符, 实际上只是简单指明了该组件可用的属性.对应了HtmlLinkageTag中的各个属性(JSF1.2) <?xml version="1.0" encoding="UTF-8"?> <taglib xsi:schemaLocation="http://java.sun.com/xml/ns/javaee webjsptaglibrary_2_1.xsd" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.1"> <description>This is the jsp tag library for my components Tcoco</description></taglib> ================================================== 第五部分(faces-config.xml文件):faces-config.xml 将组件注册到faces-config.xml中,以下是faces-config.xml文件的1.2形式 <?xml version='1.0' encoding='UTF-8'?> <faces-config version="1.2" 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_1_2.xsd"> <!-- 主要就是将你的组件的核心类声明一下就可以了,与tld文件同样都是比较简单的 --></faces-config> -------------------------------------------------------------------- 好了,以上就是制作一个组件所需要用到的各个部分,也是一个完整示例,针对于JSF1.2。 因为加入了一些javascript,所以看起来复杂了一些,可能还有一些说得不够清楚的,暂时这样吧,有需要再补充。 关于组件的使用示例,可以查看我的其它文章。 现在制作一些JSF组件还是比较繁琐麻烦的,不过JSF2.0之后可能有较大简化。期待吧! |
|
|
vieri122
2008-03-15
很少自己写组件,觉得太麻烦了,一直都是在用JBOSS的组件。
不管怎样把你这篇先收藏起来! |
|
|
eoeac
2008-06-17
按照你的文档一步布做的,但没有运行成功..传递这个组件一个menuId属性,如果不在组件类添加该属性的set方法,就报找不到menuId的set方法的错误,添加后又报
method setPageContext(PageContext) is undefined for the type MyTest 错误.. 请问您还有源码吗?或者是否能给我一个简单的例子..谢谢 一下是我的代码: public class MyTest extends UIInput {
private ValueExpression menuId;
public MyTest() {
this.setRendererType(null);
}
public void setMenuId(ValueExpression menuId) {
this.menuId = menuId;
}
//渲染该组件在页面上显示的html代码
@Override
public void encodeEnd(FacesContext fc) throws IOException {
ResponseWriter rw = fc.getResponseWriter();
//String value = (String)getAttributes().get("menuId");
rw.startElement("div", this);
//rw.writeText(value, null);
rw.writeText("哈哈", null);
rw.endElement("div");
}
}public class MyTestTag extends UIComponentELTag {
private ValueExpression menuId;
//返回组件的类型,关联到MyTest.java类
@Override
public String getComponentType() {
return "MyTest";
}
//因为组件自身渲染自己,所以不需要外部渲染器
@Override
public String getRendererType() {
return null;
}
@Override
protected void setProperties(UIComponent ui) {
super.setProperties(ui);
if(menuId != null) {
ui.setValueExpression("menuId", menuId);
}
}
@Override
public void release() {
super.release();
menuId = null;
}
public void setMenuId(ValueExpression menuId) {
this.menuId = menuId;
}
}<component> <component-type>MyTest</component-type> <component-class>com.luisl.MyTest</component-class> <attribute> <attribute-name>menuId</attribute-name> <attribute-class>java.lang.String</attribute-class> </attribute> </component> <description>MyTest</description> <display-name>Test</display-name> <short-name>Test</short-name> <tlib-version>0.9.1</tlib-version> <uri>http://www.pac.com/Test</uri> <tag> <description> 组件的名称、所关联的Tag类的全限定类名及各个可用属性, 只有在这里注册了的属性,在JSP页面中才可以使用 </description> <name>test</name> <tag-class>com.luisl.MyTest</tag-class> <attribute><name>id</name></attribute> <attribute><name>binding</name></attribute> <attribute><name>rendered</name></attribute> <attribute> <name>menuId</name> <deferred-value> <type>java.lang.String</type> </deferred-value> </attribute> </tag> <%@ page language="java" pageEncoding="GB18030"%> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://www.pac.com/Test" prefix="t" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> </head> <body> <f:view> <t:test menuId="23" /> </f:view> </body> </html> |
|
|
terryzhou
2008-06-17
顶,支持LZ..
从来只改组件没自己写过。.呵呵 |
|
|
kimmking
2008-06-19
不错不错,有时间我改改:把两个select通过facet配置进去
|

