- 浏览: 146507 次
- 来自: ...
文章分类
- 全部博客 (56)
- JSF (8)
- 励志与成功 (1)
- 数据库 (5)
- Web开发 (4)
- Solution (6)
- Spring (2)
- SVN 删除文件 (1)
- SVN 问题 (1)
- java Timer (1)
- JSP Flex (1)
- Java String (1)
- webservice (1)
- JAVA DB Connection Pool (1)
- WSDL2Java (1)
- marshaller/Unmarshaller (1)
- Refactor (1)
- JS (2)
- Java Unicode (1)
- Tiles (1)
- Struts1.x (1)
- 经理人 (1)
- Spring+Hibernate (1)
- Spring Test (1)
- Ajax Javascript (1)
- Oracle account locked (0)
- REST (2)
- REST Automation Testing (1)
- 项目管理 (2)
- JAVA开发 (2)
- Groovy (1)
- 开源软件 (1)
- Hibernate (2)
最新评论
-
hanmiao:
我在这里找到了问题的答案:https://subversion ...
SVN版本控制中的“同名受版本控制的目录已存在”问题及解决办法 -
jidu01:
没明白后面的一段,LZ 能否详细说一下呢。怎样删除XSD,又怎 ...
Unmarshaller异常javax.xml.bind.UnmarshalException: unexpected element处理方法
http://www.blogjava.net/huliqing/archive/2008/03/14/186343.html
详细分析一个JSF组件的制作过程
以下是一个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等。以便于加载这些资源
private static final String _PATH = "biz/tbuy/share/components/linkage/";
private static final String _SCRIPT = "linkage.js";
public HtmlLinkage() {
setRendererType(null); // 因为组件自身渲染html代码,所以不需要外部renderer
}
// ------------------------------------------------------------ encode
// 覆盖父类的渲染方法,这里就是渲染该组件在页面中所显示的html代码了
// 实际上就是将本来在JSP中显示的代码搬到了这里来。
@Override
public void encodeEnd(FacesContext fc) throws IOException {
ResponseWriter rw = fc.getResponseWriter();
String clientId = getClientId(fc);
encodeScript(fc); // 渲染javascript请求,你可以使用自定义的servlet处理该js请求,
encodeSelectA(rw, clientId, fc); // 渲染第一个选择框
encodeSelectB(rw, clientId, fc); // 渲染第二个选择框
encodeHidden(rw, clientId); // 泻染一个隐藏的input框,后面说明
encodeOnload(rw, clientId); // 主要用于初始化组件的状态
}
// 渲染第一个选择框
private void encodeSelectA(ResponseWriter rw, String clientId, FacesContext fc)
throws IOException {
String id = clientId + ":selectA"; // 第一个select的名称及id
String value = (String) getAttributes().get("param1");
rw.startElement("select", this);
rw.writeAttribute("name", id, null);
rw.writeAttribute("id", id, null);
if (value != null) rw.writeAttribute("value", value, null);
rw.writeAttribute("onchange", "javascript:tbuy_linkage_select(this.value);", null);
rw.writeAttribute("size", "1", null);
rw.startElement("option", this);
if (value != null) {
rw.writeAttribute("value", value, null);
rw.writeAttribute("selected", Boolean.TRUE, null);
rw.writeText(value, null);
}
rw.endElement("option");
rw.endElement("select");
}
// 渲染第二个选择框
private void encodeSelectB(ResponseWriter rw, String clientId, FacesContext fc)
throws IOException {
String id = clientId + ":selectB"; // 第二个select的名称及id
String value = (String) getAttributes().get("param2");
rw.startElement("select", this);
rw.writeAttribute("name", id, null);
rw.writeAttribute("id", id, null);
if (value != null) rw.writeAttribute("value", value, null);
rw.writeAttribute("size", "1", null);
rw.writeAttribute("style", "margin-left:5px;", null);
rw.startElement("option", this);
if (value != null) {
rw.writeAttribute("value", value, null);
rw.writeAttribute("selected", Boolean.TRUE, null);
rw.writeText(value, null);
}
rw.endElement("option");
rw.endElement("select");
}
// 渲染一个隐藏字段,该隐藏字段主要用于在页面存放所有可选项(有规律的字符串),
// 即是将所有可用的选项组织成了一个有规律的字符串,存放于隐藏域中,以便js进行分析,最后的字符串的形式像这样(objStr)
//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;
private void encodeHidden(ResponseWriter rw, String clientId) throws IOException {
// 将页面中绑定的Map类型的所有选顶转化为字符串形式,页面中的组件使用像是这样的:
// <hu:linkage ... obj="#{TestBean.val2}"/>
Object obj = this.getAttributes().get("obj"); // 也就是获取组件的obj属性所绑定的值
String objStr = getAsString(obj); // 将obj转化为字符串形式存放于隐藏域中
rw.startElement("input", this);
rw.writeAttribute("id", clientId + ":obj", null);
rw.writeAttribute("name", clientId + ":obj", null);
rw.writeAttribute("type", "hidden", null);
rw.writeAttribute("value", objStr, null);
rw.endElement("input");
}
// 渲染一个js函数调用,在该组件渲染完毕之后调用tbuy_linkage_init对组件进行初始化
private void encodeOnload(ResponseWriter rw, String clientId) throws IOException {
rw.startElement("script", this);
rw.writeAttribute("type", "text/javascript", null);
rw.writeText("tbuy_linkage_init('" + clientId + "')", null);
rw.endElement("script");
}
// 渲染一个JS文件的请求,实际上你也可以直接渲染所有js代码,但那样会很麻烦。
private void encodeScript(FacesContext fc) throws IOException {
ResponseWriter rw = fc.getResponseWriter();
String contentPath = fc.getExternalContext().getRequestContextPath();
rw.startElement("script", this);
rw.writeAttribute("type", "text/javascript", null);
rw.writeAttribute("src", contentPath +
TbuyExtensionsFilter.TBUY_EXT_ID +
TbuyExtensionsFilter.REQUEST_SCRIPT + "=" +
_PATH + _SCRIPT, null);
rw.endElement("script");
}
// -------------------------------------------------- process
// 该方法主要是将组件的obj属性所绑定的值(所有可选项)转化为字符串,转化后的字符串像是这样的
//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;
private String getAsString(Object obj) {
StringBuilder str = new StringBuilder();
if (obj instanceof Map) {
Map objm = (Map) obj;
Set keys = objm.keySet();
Iterator iter = keys.iterator();
while (iter.hasNext()) {
Object temp = iter.next();
String[] keyArr = getAsStringArr(temp); // 处理第一维选顶,一律转为String[]
str.append(keyArr[0] + "," + keyArr[1] + "==");
Object valueObj = objm.get(temp);
List<String[]> values = getAsList(valueObj);// 处理第二维
for (String[] val : values) {
str.append(val[0] + "," + val[1] + ";");
}
str.append("@@");
}
}
return str.toString();
}
// 这里相当于一个适配器,将String数组或String类型都转化成String数组返回
private String[] getAsStringArr(Object obj) {
if (obj instanceof String[]) {
return ((String[]) obj);
} else if (obj instanceof String) {
String tt = (String) obj;
return (new String[]{tt,tt});
}
return null;
}
// 参照上面
private List<String[]> getAsList(Object obj) {
List<String[]> strArr = new ArrayList<String[]>();
if (obj instanceof List) {
List temp = (List) obj;
Iterator iter = temp.iterator();
while (iter.hasNext()) {
Object tt = iter.next();
String[] val = getAsStringArr(tt);
strArr.add(val);
}
return strArr;
}
return null;
}
// -------------------------------------------------- decode
// 组件的解码,该方法在页面有回传(比如提交了表单form)的时候才会执行。
@Override
public void decode(FacesContext fc) {
String clientId = getClientId(fc);// 获取当前组件的页面id
Map<String, String> values = fc.getExternalContext().getRequestParameterMap();
Map<String, String> val = new HashMap<String, String>(2);
String valA = values.get(clientId + ":selectA"); // 获取两个select的当前所选的值
String valB = values.get(clientId + ":selectB");
this.setSubmittedValue(val);
this.getValueExpression("param1").setValue(fc.getELContext(), valA);// 将值绑定到param1所绑定的后台bean的属性
this.getValueExpression("param2").setValue(fc.getELContext(), valB);// 同上
}
}
================================================== 第二部分(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页面中的三个自定义属性
private ValueExpression param2;
private ValueExpression obj;
@Override
public String getComponentType() {
return "HtmlLinkage"; // 返回组件的类型,关联到了上面的HtmlLinkage
}
@Override
public String getRendererType() {
return null; // 因为组件自身渲染自己,所以不需要外部渲染器
}
@Override
protected void setProperties(UIComponent ui) {
super.setProperties(ui); // 先调用父类处理组件的已有属性如id,binding,redered...
setValueBinding(ui, "param1", param1); // 处理并将三个自定义属性绑定到组件的attribute中
setValueBinding(ui, "param2", param2);
setValueBinding(ui, "obj", obj);
}
private void setValueBinding(UIComponent ui, String displayName, ValueExpression value) {
if (value != null) {
ui.setValueExpression(displayName, value); // 绑定属性
}
}
// ------------------------------------------------------------ setter
// 这几个方法是必须的,否则你的jsp页面中就无法使用相应的属性,
// 会出现找不到setter方法的错误。
public void setParam1(ValueExpression param1) {
this.param1 = param1;
}
public void setParam2(ValueExpression param2) {
this.param2 = param2;
}
public void setObj(ValueExpression obj) {
this.obj = obj;
}
@Override
public void release() {
super.release(); // 释放资源
this.param1 = null;
this.param2 = null;
this.obj = null;
}
}
================================================== 第三部分(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;
// 获取隐藏域的值,该隐藏域在上面已经有说明,存放所有可选项的字符串
var str = document.getElementById(tbuy_linkage_clientId + ":obj").value;
// 获取页面中的两个select
tbuy_linkage_selectA = document.getElementById(tbuy_linkage_clientId + ":selectA");
tbuy_linkage_selectB = document.getElementById(tbuy_linkage_clientId + ":selectB");
// 分析字符串,并存放于tbuy_linkage_params中
tbuy_linkage_parse(str);
var valueA = tbuy_linkage_selectA.value; // 原始值A
var valueB = tbuy_linkage_selectB.value; // 原始值B
tbuy_linkage_clearSelectA(); // 清除A框
tbuy_linkage_setSelectA(); // 重设A框
// 如果页面经过了回传请求,则需要将selectA的值设置回上一次所选择的值,否则每一次请求都会重置
if (valueA != null) {
tbuy_linkage_selectA.value = valueA;
}
// 根椐selectA框的值设置selectB框的值。
tbuy_linkage_select(document.getElementById(tbuy_linkage_clientId + ":selectA").value);
if (valueB != null) tbuy_linkage_selectB.value = valueB; // 同上
}
}
// 初始化A选择框的各选项
function tbuy_linkage_setSelectA() {
for (var i = 0; i < tbuy_linkage_params.length; i++) {
var temp = tbuy_linkage_params[i];
var opt = new Option();
opt.value = temp.value;
opt.text = temp.text;
tbuy_linkage_selectA.options.add(opt);
}
}
// 选择selectA框后根据其值设置selectB的值
function tbuy_linkage_select(val) {
for (var i = 0; i < tbuy_linkage_params.length; i++) {
var selA = tbuy_linkage_params[i];
if (selA.value == val) {
tbuy_linkage_clearSelectB(); // 清除selectB的值并重设
tbuy_linkage_setSelectB(selA.objArr);
}
}
}
// 该方法用于清除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];
var opt = new Option();
opt.value = obj.value;
opt.text = obj.text;
tbuy_linkage_selectB.options.add(opt);
}
}
// 该方法用于分析隐藏域中的值,即所有可选类型的字符串形式.字符串规律像是这样(可参照核心类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();
strArr = str.split("@@");
for (var i = 0; i < strArr.length - 1; i++) {
var lineArr = strArr[i].split("==");
var objKVArr = lineArr[0].split(",");
var param = new Object();
param.value = objKVArr[0];
param.text = objKVArr[1];
// 分析"=="号后面的子选项,即第二维的选项
param.objArr = tbuy_linkage_getAsArray(lineArr[1]);
tbuy_linkage_params[i] = param;
}
}
// 配合上面的分析
function tbuy_linkage_getAsArray(valueStr) {
var objArr = new Array();
var valArr = new Array();
valArr = valueStr.split(";");
for (var i = 0; i < valArr.length - 1; i++) {
var selArr = valArr[i].split(",");
var param = new Object();
param.value = selArr[0];
param.text = selArr[1];
objArr[i] = param;
}
return objArr;
}
================================================== 第四部分(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>
<!-- 给自己指定一个组件库名称 -->
<display-name>Tcoco</display-name>
<short-name>Tcoco</short-name>
<!-- 给组件库指定一个版本 -->
<tlib-version>0.9.1</tlib-version>
<!-- 指定一个uri,该uri指定了使用该组件库所需要引入到jsp页面中的标签,你可以指定其它的,
但在jsp页面中的uri必须与这里匹配,如:
<%@taglib prefix="coco" uri="http://www.tbuy.biz/Tcoco"%>
<%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%>
<%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%>
-->
<uri>http://www.tbuy.biz/Tcoco</uri>
<tag>
<description>
组件的名称、所关联的Tag类的全限定类名及各个可用属性,
只有在这里注册了的属性,在JSP页面中才可以使用
</description>
<name>linkage</name>
<tag-class>
biz.tbuy.share.components.linkage.HtmlLinkageTag
</tag-class>
<attribute>
<name>id</name>
</attribute>
<attribute>
<name>binding</name>
</attribute>
<attribute>
<name>rendered</name>
</attribute>
<attribute>
<name>param1</name>
<deferred-value>
<type>java.lang.String</type>
</deferred-value>
</attribute>
<attribute>
<name>param2</name>
<deferred-value>
<type>java.lang.String</type>
</deferred-value>
</attribute>
<attribute>
<name>obj</name>
<deferred-value>
<type>java.lang.Object</type>
</deferred-value>
</attribute>
</tag>
</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文件同样都是比较简单的 -->
<component>
<component-type>HtmlLinkage</component-type>
<component-class>
biz.tbuy.share.components.linkage.HtmlLinkage
</component-class>
</component>
</faces-config>
--------------------------------------------------------------------
好了,以上就是制作一个组件所需要用到的各个部分,也是一个完整示例,针对于JSF1.2。
因为加入了一些javascript,所以看起来复杂了一些,可能还有一些说得不够清楚的,暂时这样吧,有需要再补充。
关于组件的使用示例,可以查看我的其它文章。
现在制作一些JSF组件还是比较繁琐麻烦的,不过JSF2.0之后可能有较大简化。期待吧!
详细分析一个JSF组件的制作过程
以下是一个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等。以便于加载这些资源
private static final String _PATH = "biz/tbuy/share/components/linkage/";
private static final String _SCRIPT = "linkage.js";
public HtmlLinkage() {
setRendererType(null); // 因为组件自身渲染html代码,所以不需要外部renderer
}
// ------------------------------------------------------------ encode
// 覆盖父类的渲染方法,这里就是渲染该组件在页面中所显示的html代码了
// 实际上就是将本来在JSP中显示的代码搬到了这里来。
@Override
public void encodeEnd(FacesContext fc) throws IOException {
ResponseWriter rw = fc.getResponseWriter();
String clientId = getClientId(fc);
encodeScript(fc); // 渲染javascript请求,你可以使用自定义的servlet处理该js请求,
encodeSelectA(rw, clientId, fc); // 渲染第一个选择框
encodeSelectB(rw, clientId, fc); // 渲染第二个选择框
encodeHidden(rw, clientId); // 泻染一个隐藏的input框,后面说明
encodeOnload(rw, clientId); // 主要用于初始化组件的状态
}
// 渲染第一个选择框
private void encodeSelectA(ResponseWriter rw, String clientId, FacesContext fc)
throws IOException {
String id = clientId + ":selectA"; // 第一个select的名称及id
String value = (String) getAttributes().get("param1");
rw.startElement("select", this);
rw.writeAttribute("name", id, null);
rw.writeAttribute("id", id, null);
if (value != null) rw.writeAttribute("value", value, null);
rw.writeAttribute("onchange", "javascript:tbuy_linkage_select(this.value);", null);
rw.writeAttribute("size", "1", null);
rw.startElement("option", this);
if (value != null) {
rw.writeAttribute("value", value, null);
rw.writeAttribute("selected", Boolean.TRUE, null);
rw.writeText(value, null);
}
rw.endElement("option");
rw.endElement("select");
}
// 渲染第二个选择框
private void encodeSelectB(ResponseWriter rw, String clientId, FacesContext fc)
throws IOException {
String id = clientId + ":selectB"; // 第二个select的名称及id
String value = (String) getAttributes().get("param2");
rw.startElement("select", this);
rw.writeAttribute("name", id, null);
rw.writeAttribute("id", id, null);
if (value != null) rw.writeAttribute("value", value, null);
rw.writeAttribute("size", "1", null);
rw.writeAttribute("style", "margin-left:5px;", null);
rw.startElement("option", this);
if (value != null) {
rw.writeAttribute("value", value, null);
rw.writeAttribute("selected", Boolean.TRUE, null);
rw.writeText(value, null);
}
rw.endElement("option");
rw.endElement("select");
}
// 渲染一个隐藏字段,该隐藏字段主要用于在页面存放所有可选项(有规律的字符串),
// 即是将所有可用的选项组织成了一个有规律的字符串,存放于隐藏域中,以便js进行分析,最后的字符串的形式像这样(objStr)
//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;
private void encodeHidden(ResponseWriter rw, String clientId) throws IOException {
// 将页面中绑定的Map类型的所有选顶转化为字符串形式,页面中的组件使用像是这样的:
// <hu:linkage ... obj="#{TestBean.val2}"/>
Object obj = this.getAttributes().get("obj"); // 也就是获取组件的obj属性所绑定的值
String objStr = getAsString(obj); // 将obj转化为字符串形式存放于隐藏域中
rw.startElement("input", this);
rw.writeAttribute("id", clientId + ":obj", null);
rw.writeAttribute("name", clientId + ":obj", null);
rw.writeAttribute("type", "hidden", null);
rw.writeAttribute("value", objStr, null);
rw.endElement("input");
}
// 渲染一个js函数调用,在该组件渲染完毕之后调用tbuy_linkage_init对组件进行初始化
private void encodeOnload(ResponseWriter rw, String clientId) throws IOException {
rw.startElement("script", this);
rw.writeAttribute("type", "text/javascript", null);
rw.writeText("tbuy_linkage_init('" + clientId + "')", null);
rw.endElement("script");
}
// 渲染一个JS文件的请求,实际上你也可以直接渲染所有js代码,但那样会很麻烦。
private void encodeScript(FacesContext fc) throws IOException {
ResponseWriter rw = fc.getResponseWriter();
String contentPath = fc.getExternalContext().getRequestContextPath();
rw.startElement("script", this);
rw.writeAttribute("type", "text/javascript", null);
rw.writeAttribute("src", contentPath +
TbuyExtensionsFilter.TBUY_EXT_ID +
TbuyExtensionsFilter.REQUEST_SCRIPT + "=" +
_PATH + _SCRIPT, null);
rw.endElement("script");
}
// -------------------------------------------------- process
// 该方法主要是将组件的obj属性所绑定的值(所有可选项)转化为字符串,转化后的字符串像是这样的
//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;
private String getAsString(Object obj) {
StringBuilder str = new StringBuilder();
if (obj instanceof Map) {
Map objm = (Map) obj;
Set keys = objm.keySet();
Iterator iter = keys.iterator();
while (iter.hasNext()) {
Object temp = iter.next();
String[] keyArr = getAsStringArr(temp); // 处理第一维选顶,一律转为String[]
str.append(keyArr[0] + "," + keyArr[1] + "==");
Object valueObj = objm.get(temp);
List<String[]> values = getAsList(valueObj);// 处理第二维
for (String[] val : values) {
str.append(val[0] + "," + val[1] + ";");
}
str.append("@@");
}
}
return str.toString();
}
// 这里相当于一个适配器,将String数组或String类型都转化成String数组返回
private String[] getAsStringArr(Object obj) {
if (obj instanceof String[]) {
return ((String[]) obj);
} else if (obj instanceof String) {
String tt = (String) obj;
return (new String[]{tt,tt});
}
return null;
}
// 参照上面
private List<String[]> getAsList(Object obj) {
List<String[]> strArr = new ArrayList<String[]>();
if (obj instanceof List) {
List temp = (List) obj;
Iterator iter = temp.iterator();
while (iter.hasNext()) {
Object tt = iter.next();
String[] val = getAsStringArr(tt);
strArr.add(val);
}
return strArr;
}
return null;
}
// -------------------------------------------------- decode
// 组件的解码,该方法在页面有回传(比如提交了表单form)的时候才会执行。
@Override
public void decode(FacesContext fc) {
String clientId = getClientId(fc);// 获取当前组件的页面id
Map<String, String> values = fc.getExternalContext().getRequestParameterMap();
Map<String, String> val = new HashMap<String, String>(2);
String valA = values.get(clientId + ":selectA"); // 获取两个select的当前所选的值
String valB = values.get(clientId + ":selectB");
this.setSubmittedValue(val);
this.getValueExpression("param1").setValue(fc.getELContext(), valA);// 将值绑定到param1所绑定的后台bean的属性
this.getValueExpression("param2").setValue(fc.getELContext(), valB);// 同上
}
}
================================================== 第二部分(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页面中的三个自定义属性
private ValueExpression param2;
private ValueExpression obj;
@Override
public String getComponentType() {
return "HtmlLinkage"; // 返回组件的类型,关联到了上面的HtmlLinkage
}
@Override
public String getRendererType() {
return null; // 因为组件自身渲染自己,所以不需要外部渲染器
}
@Override
protected void setProperties(UIComponent ui) {
super.setProperties(ui); // 先调用父类处理组件的已有属性如id,binding,redered...
setValueBinding(ui, "param1", param1); // 处理并将三个自定义属性绑定到组件的attribute中
setValueBinding(ui, "param2", param2);
setValueBinding(ui, "obj", obj);
}
private void setValueBinding(UIComponent ui, String displayName, ValueExpression value) {
if (value != null) {
ui.setValueExpression(displayName, value); // 绑定属性
}
}
// ------------------------------------------------------------ setter
// 这几个方法是必须的,否则你的jsp页面中就无法使用相应的属性,
// 会出现找不到setter方法的错误。
public void setParam1(ValueExpression param1) {
this.param1 = param1;
}
public void setParam2(ValueExpression param2) {
this.param2 = param2;
}
public void setObj(ValueExpression obj) {
this.obj = obj;
}
@Override
public void release() {
super.release(); // 释放资源
this.param1 = null;
this.param2 = null;
this.obj = null;
}
}
================================================== 第三部分(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;
// 获取隐藏域的值,该隐藏域在上面已经有说明,存放所有可选项的字符串
var str = document.getElementById(tbuy_linkage_clientId + ":obj").value;
// 获取页面中的两个select
tbuy_linkage_selectA = document.getElementById(tbuy_linkage_clientId + ":selectA");
tbuy_linkage_selectB = document.getElementById(tbuy_linkage_clientId + ":selectB");
// 分析字符串,并存放于tbuy_linkage_params中
tbuy_linkage_parse(str);
var valueA = tbuy_linkage_selectA.value; // 原始值A
var valueB = tbuy_linkage_selectB.value; // 原始值B
tbuy_linkage_clearSelectA(); // 清除A框
tbuy_linkage_setSelectA(); // 重设A框
// 如果页面经过了回传请求,则需要将selectA的值设置回上一次所选择的值,否则每一次请求都会重置
if (valueA != null) {
tbuy_linkage_selectA.value = valueA;
}
// 根椐selectA框的值设置selectB框的值。
tbuy_linkage_select(document.getElementById(tbuy_linkage_clientId + ":selectA").value);
if (valueB != null) tbuy_linkage_selectB.value = valueB; // 同上
}
}
// 初始化A选择框的各选项
function tbuy_linkage_setSelectA() {
for (var i = 0; i < tbuy_linkage_params.length; i++) {
var temp = tbuy_linkage_params[i];
var opt = new Option();
opt.value = temp.value;
opt.text = temp.text;
tbuy_linkage_selectA.options.add(opt);
}
}
// 选择selectA框后根据其值设置selectB的值
function tbuy_linkage_select(val) {
for (var i = 0; i < tbuy_linkage_params.length; i++) {
var selA = tbuy_linkage_params[i];
if (selA.value == val) {
tbuy_linkage_clearSelectB(); // 清除selectB的值并重设
tbuy_linkage_setSelectB(selA.objArr);
}
}
}
// 该方法用于清除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];
var opt = new Option();
opt.value = obj.value;
opt.text = obj.text;
tbuy_linkage_selectB.options.add(opt);
}
}
// 该方法用于分析隐藏域中的值,即所有可选类型的字符串形式.字符串规律像是这样(可参照核心类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();
strArr = str.split("@@");
for (var i = 0; i < strArr.length - 1; i++) {
var lineArr = strArr[i].split("==");
var objKVArr = lineArr[0].split(",");
var param = new Object();
param.value = objKVArr[0];
param.text = objKVArr[1];
// 分析"=="号后面的子选项,即第二维的选项
param.objArr = tbuy_linkage_getAsArray(lineArr[1]);
tbuy_linkage_params[i] = param;
}
}
// 配合上面的分析
function tbuy_linkage_getAsArray(valueStr) {
var objArr = new Array();
var valArr = new Array();
valArr = valueStr.split(";");
for (var i = 0; i < valArr.length - 1; i++) {
var selArr = valArr[i].split(",");
var param = new Object();
param.value = selArr[0];
param.text = selArr[1];
objArr[i] = param;
}
return objArr;
}
================================================== 第四部分(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>
<!-- 给自己指定一个组件库名称 -->
<display-name>Tcoco</display-name>
<short-name>Tcoco</short-name>
<!-- 给组件库指定一个版本 -->
<tlib-version>0.9.1</tlib-version>
<!-- 指定一个uri,该uri指定了使用该组件库所需要引入到jsp页面中的标签,你可以指定其它的,
但在jsp页面中的uri必须与这里匹配,如:
<%@taglib prefix="coco" uri="http://www.tbuy.biz/Tcoco"%>
<%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%>
<%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%>
-->
<uri>http://www.tbuy.biz/Tcoco</uri>
<tag>
<description>
组件的名称、所关联的Tag类的全限定类名及各个可用属性,
只有在这里注册了的属性,在JSP页面中才可以使用
</description>
<name>linkage</name>
<tag-class>
biz.tbuy.share.components.linkage.HtmlLinkageTag
</tag-class>
<attribute>
<name>id</name>
</attribute>
<attribute>
<name>binding</name>
</attribute>
<attribute>
<name>rendered</name>
</attribute>
<attribute>
<name>param1</name>
<deferred-value>
<type>java.lang.String</type>
</deferred-value>
</attribute>
<attribute>
<name>param2</name>
<deferred-value>
<type>java.lang.String</type>
</deferred-value>
</attribute>
<attribute>
<name>obj</name>
<deferred-value>
<type>java.lang.Object</type>
</deferred-value>
</attribute>
</tag>
</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文件同样都是比较简单的 -->
<component>
<component-type>HtmlLinkage</component-type>
<component-class>
biz.tbuy.share.components.linkage.HtmlLinkage
</component-class>
</component>
</faces-config>
--------------------------------------------------------------------
好了,以上就是制作一个组件所需要用到的各个部分,也是一个完整示例,针对于JSF1.2。
因为加入了一些javascript,所以看起来复杂了一些,可能还有一些说得不够清楚的,暂时这样吧,有需要再补充。
关于组件的使用示例,可以查看我的其它文章。
现在制作一些JSF组件还是比较繁琐麻烦的,不过JSF2.0之后可能有较大简化。期待吧!
发表评论
-
JSF1与JSF2的区别
2015-05-21 10:19 895很多年前用过JSF,当时还是1.2版本。现在JSF最新的版 ... -
JSF1.2自定义组件中EL表达式的处理。
2009-08-12 18:56 2134在基于Eclipse(MyEclipse)+Tomcat6的开 ... -
create MethodBinding MethodExpression ValueExpression
2009-08-12 16:02 18761. create MethodBinding MethodB ... -
Handle Event in JSF custom tag (1.2)
2009-08-12 15:08 1351the key for event hanldling in ... -
get Spring bean from application context in JSF
2009-08-04 15:19 1426JSF Renderer to get bean in Spr ... -
Trinidad中导出文件异常JSF导出文件异常getOutputStream() has already been called的解决方法
2009-08-01 23:34 1920使用Trinidad导出CSV/PDF文件时,会遇到g ... -
javascript in JSF
2009-07-29 17:09 1046<f:verbatim> <scrip ...
相关推荐
**JSF自定义组件开发详解** JavaServer Faces (JSF) 是一个用于构建Web应用程序的Java框架,它提供了一种模型-视图-控制器(MVC)架构来简化开发过程。在JSF中,自定义组件是扩展其功能的关键部分,允许开发者根据...
【JSF自定义组件实现详解】 在JavaServer Faces (JSF)框架中,自定义组件允许开发者根据项目需求创建具有特定功能的UI元素。本文将引导你逐步了解如何在JSF中实现一个自定义组件,从基础开始,一步步构建一个简单的...
虽然没有具体的压缩包文件内容可供参考,但从标题和标签中,我们可以推测这篇博客主要涵盖了JSF自定义组件的创建、配置、使用和测试等方面的知识,对于想要深入理解JSF框架和提升组件开发能力的开发者来说,是非常有...
在JavaServer Faces (JSF) 2.0中,自定义组件是开发人员扩展框架功能的关键方式。通过创建自定义组件,你可以构建符合特定需求的用户界面元素,这些元素可以复用并集成到多个JSF应用程序中。本教程将深入探讨如何在...
这个过程展示了完整的JSF2自定义组件开发流程,对于希望深入理解JSF2组件化的开发者来说非常有价值。通过学习这些内容,开发者可以更好地理解和应用JSF2的强大功能,提升Web应用的开发效率和质量。
在JSF中,自定义组件允许开发人员扩展框架的功能,以满足特定项目的需求。下面将详细解释如何创建一个自定义JSF组件,包括组件的组成部分、标签库描述、属性处理类和组件类的创建。 一、自定义组件组成 一个完整的...
在JSF 2.0中,自定义组件的开发变得更加方便,通过利用注解和Facelets的特性,可以快速地创建和部署组件。这使得JSF成为了一个强大且灵活的Web开发框架,为开发人员提供了构建企业级应用程序的高效工具。
jsf官方实现默认是不支持multipart/form-data类型的表单的,为了使jsf能够解析multipart/form-data表单的数据,我参考了myfaces1.1的方式,然后按照commons-fileupload1.2...fileupload1.1)开发了一个文件上传组件。
注册这两个自定义组件也很简单。在JSF页面(`.xhtml`文件)中,你可以通过`converter`和`validator`属性指定它们,如下所示: ```html ``` 在这个例子中,`customIntegerConverter`是自定义转换器的ID,`...
JSF允许开发者创建自定义组件,以满足特定需求。你可以通过继承`UIComponent`并实现必要的生命周期方法来构建一个分页组件。这个组件可以包含页码选择器和导航按钮,同时需要与后端数据模型进行交互,以实现分页逻辑...
在"jsf组件开发源码"中,我们很可能是要探讨如何创建自定义的JSF组件,以及源码背后的实现逻辑。 首先,JSF组件是由UIComponent类及其子类构成的。每个组件都是一个独立的UI元素,具有渲染、属性和事件处理能力。...
在"JSF组件开发.rar"这个压缩包中,我们很可能会找到关于如何设计、实现和使用JSF自定义组件的详细教程或指南。 ### 一、JSF组件基础 1. **组件层次结构**:JSF组件树是应用程序的核心,每个组件都有属性、事件和...
自定义组件的完整开发流程还包括注册组件到JSF的组件库,编写渲染器来决定组件如何在HTML中呈现,以及在faces-config.xml配置文件中声明组件和渲染器。通过这种方式,开发者可以创建出符合特定业务需求的自定义JSF...
自定义组件是JSF的强大特性之一,它允许开发者扩展JSF的默认组件库,以满足特定的应用需求。 **自定义组件的构成**: 1. **UIComponent**: 自定义组件的基础是UIComponent类,它是所有JSF组件的基类。它包含了一...
为了深入学习JSF和自定义组件开发,以下是一些有价值的资源: 1. **JSF官方文档** - 提供了详尽的规范和API文档。 2. **JSF Tutorials** - 包括Sun的官方教程和社区贡献的指导文章。 3. **JSF Forums and ...
大多数现代IDE(如Eclipse、NetBeans或IntelliJ IDEA)都内置了对JSF的支持,使得组件开发变得更加直观和简单。IDE通常会提供一个可视化的界面,其中包含各种JSF组件,只需通过简单的操作即可完成组件的添加和属性...
2. **自定义组件开发**:在JSF 1.2中,开发者可以通过实现UIComponent接口并重写必要的方法来创建自定义组件。组件需要定义属性、事件处理和渲染逻辑。此外,还需要提供一个对应的标签库描述文件(TLD或XML)以便在...
在JSF中,我们可以通过自定义组件或者使用现有的库如PrimeFaces或RichFaces来实现这些功能。 **2. 自定义JSF分页组件** 创建自定义分页组件涉及以下几个步骤: - **设计组件**: 首先,你需要设计一个UI组件,包含...
JSF不仅支持标准的组件库,还允许开发者创建自定义组件。组件开发人员通常关注于生命周期的起始阶段和结束阶段: - **恢复视图**(Restore View): 在这一阶段,组件开发人员需要确保视图正确构建,事件处理器和...