• 沒有找到結果。

组件标签

在文檔中 语言技术: JavaServer Faces (頁 91-105)

3 自订组件

3.2 简单实例

3.2.2 组件标签

在我们的例子中,我们都是处理字符串对象,所以这边不需要转换器,如果您需要使用转 换器,可以呼叫 setConverter()方法加以设定,在不使用 Renderer 的时候,Component 要设定转换器来自行进行字符串与对象的转换。

3.2.2 组件标签

完成 Component 的自订,接下来要设定一个自订 Tag 与之对应,自订 Tag 的目的,在于设 定 Component 属性,取得 Componenty 型态,取得 Renderer 型态值等;属性的设定包括了 设定静态值、设定绑定值、设定验证器等等。

要自订与 Component 对应的 Tag,您可以继承 UIComponentTag,例如:

• TextWithCmdTag.java

package onlyfun.caterpillar;

import javax.faces.application.Application;

import javax.faces.component.UIComponent;

import javax.faces.context.FacesContext;

import javax.faces.el.ValueBinding;

import javax.faces.webapp.UIComponentTag;

public class TextWithCmdTag extends UIComponentTag { private String size;

private String value;

public String getComponentType() {

return "onlyfun.caterpillar.TextWithCmd";

}

public String getRendererType() {

return null;

}

public void setProperties(UIComponent component) { super.setProperties(component);

private void setStringProperty(UIComponent

component,

public void release() { super.release();

size = null;

value = null;

}

public String getSize() { return size;

}

public void setSize(String size) { this.size = size;

}

public String getValue() { return value;

}

public void setValue(String value) { this.value = value;

} }

首先看到这两个方法:

public String getComponentType() {

return "onlyfun.caterpillar.TextWithCmd";

}

public String getRendererType() { return null;

}

由于我们的 Component 目前不使用 Renderer,所以 getRendererType()传回 null 值,而

getComponentType()在于让 JSF 取得这个 Tag 所对应的 Component,所传回的值在

<component-class>

onlyfun.caterpillar.UITextWithCmd </component-class>

</component>

....

藉由 faces-config.xml 中的定义,JSF 可以得知 onlyfun.caterpillar.TextWithCmd 的真 正类别,而这样的定义方式很显然的,您可以随时换掉<component- class>所对应的类别,

也就是说,Tag 所对应的 Component 是可以随时替换的。

在设定 Component 属性值时,可以由 component.getAttributes()取得 Map 对象,并将标签 属性值存入 Map 中,这个 Map 对象可以在对应的 Component 中使用 getAttributes()取得,

例如在上一个主题中的 UITextWithCmd 中可以如下取得存入 Map 的 size 属性:

package onlyfun.caterpillar;

import java.io.IOException;

import java.util.Map;

import javax.faces.component.UIInput;

import javax.faces.context.FacesContext;

import javax.faces.context.ResponseWriter;

public class UITextWithCmd extends UIInput { ....

private void encodeTextField(ResponseWriter writer, String clientId) throws IOException {

可以使用 isValueReference()来测试是否为 JSF Expression Language 的绑定语法,如果 是的话,则我们必须建立 ValueBinding 对象,并设定值绑定:

....

private void setStringProperty(UIComponent component, String attrName, String attrValue) {

如果是 value 属性,记得在上一个主题中我们提过,从 UIOutput 继承下来的 getValue() 方法可以取得 Component 的 value 设定值,这个值可能是静态的属性设定值,也可能是 JSF Expression 的绑定值,预设会先从组件的属性设定值开始找寻,如果找不到,再从绑定值

(ValueBinding 对象)中找寻。

最后,我们必须提供自订 Tag 的 tld 檔:

• textcmd.tld

<?xml version="1.0" encoding="UTF-8"?>

<taglib version="2.0"

xmlns="http://java.sun.com/xml/ns/j2ee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance

"

xsi:schemaLocation=

"http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2 _0.xsd">

<tlib-version>1.0</tlib-version>

<jsp-version>2.0</jsp-version>

<short-name>textcmd</short-name>

<uri>http://caterpillar.onlyfun.net/textcmd</uri>

<tag>

<name>textcmd</name>

<tag-class>onlyfun.caterpillar.TextWithCmdTag</tag-c lass>

<body-content>empty</body-content>

<attribute>

<name>size</name>

</attribute>

<attribute>

<name>value</name>

<required>true</required>

</attribute>

</tag>

</taglib>

3.2.3 使用自订组件

在 Component 与 Tag 自订完成后,这边来看看如何使用它们,首先定义 faces-config.xml:

• faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"

"http://java.sun.com/dtd/web-facesconfig_1_0.dtd">

<faces-config>

<component>

<component-type>

onlyfun.caterpillar.TextWithCmd </component-type>

<component-class>

onlyfun.caterpillar.UITextWithCmd </component-class>

</component>

<managed-bean>

<managed-bean-name>someBean</managed-bean-name>

<managed-bean-class>

onlyfun.caterpillar.SomeBean </managed-bean-class>

<managed-bean-scope>session</managed-bean-scope>

</managed-bean>

</faces-config>

<component>中定义 Component 的型态与实际的类别对应,在您于自订 Tag 中呼叫 getComponentType()方法所返回的值,就是寻找<component-type>设定的值对应,并由此 得知真正对应的 Component 类别。

我们所撰写的 SomeBean 测试类别如下:

• SomeBean.java

package onlyfun.caterpillar;

public class SomeBean { private String data;

public String getData() { return data;

}

public void setData(String data) { this.data = data;

} }

这边写一个简单的网页来测试一下我们撰写的自订组件:

• index.jsp

<%@ taglib uri="http://java.sun.com/jsf/html"

prefix="h" %>

<%@ taglib uri="http://java.sun.com/jsf/core"

prefix="f" %>

<%@ taglib uri="/WEB-INF/textcmd.tld" prefix="oc" %>

<html>

<link href="styles.css" rel="stylesheet"

type="text/css"/>

<head>

<title></title>

</head>

<body>

<f:view>

<h:form>

Input data: <oc:textcmd size="10"

value="#{someBean.data}"/>

</h:form>

<h:outputText value="#{someBean.data}"/>

</f:view>

</body>

</html>

3.2.4 自订 Renderer

Component 可以将译码、编码的动作交给 Renderer,这让您的表现层技术可以轻易的抽换,

我们可以将之前的自订组件的译码、编码动作移出至 Renderer,不过由于我们之前设计的 Component 是个很简单的组件,事实上,如果只是要新增一个 Command 在输入字段旁边,我 们并不需要大费周章的自订一个新的组件,我们可以直接为输入字段更换一个自订的 Renderer。

要自订一个 Renderer,您要继承 javax.faces.render.Renderer,我们的自订 Renderer 如 下:

• TextCmdRenderer.java

package onlyfun.caterpillar;

import java.io.IOException;

import java.util.Map;

import javax.faces.component.EditableValueHolder;

import javax.faces.component.UIComponent;

import javax.faces.component.UIInput;

import javax.faces.context.FacesContext;

import javax.faces.context.ResponseWriter;

import javax.faces.render.Renderer;

public class TextCmdRenderer extends Renderer { private static final String TEXT = ".text";

private static final String CMD = ".cmd";

public void encodeBegin(FacesContext context, UIComponent component) throws

IOException {

ResponseWriter writer =

context.getResponseWriter();

private void encodeTextField(UIComponent component,

ResponseWriter writer, String clientId)

throws IOException {

writer.startElement("input", component);

writer.writeAttribute("name", clientId + TEXT,

private void encodeCommand(UIComponent component, ResponseWriter writer,

String clientId) throws IOException {

writer.startElement("input", component);

writer.writeAttribute("type", "submit", null);

writer.writeAttribute("name", clientId + CMD, null);

writer.writeAttribute("value", "submit", null);

writer.endElement("input");

}

}

这个自订的 Renderer 其译码、编码过程,与之前直接在 Component 中进行译码或编码过程 是类似的,所不同的是在译码与编码的方法上,多了 UIComponent 参数,代表所代理绘制 的 Component。

接下来在自订 Tag 上,我们的 TextWithCmdTag 与之前主题所介绍的没什么差别,只不过在 getComponentType()与 getRendererType()方法上要修改一下:

• TextWithCmdTag.java

package onlyfun.caterpillar;

import javax.faces.application.Application;

import javax.faces.component.UIComponent;

import javax.faces.context.FacesContext;

import javax.faces.el.ValueBinding;

import javax.faces.webapp.UIComponentTag;

public class TextWithCmdTag extends UIComponentTag { private String size;

private String value;

public String getComponentType() { return "javax.faces.Input";

}

public String getRendererType() {

return "onlyfun.caterpillar.TextCmd";

} ...

}

getComponentType()取得的是"javax.faces.Input",它实际上对应至 UIInput 类别,而 getRendererType()取回的是"onlyfun.caterpillar.TextCmd",这会在 faces-config.xml 中定义,以对应至实际的 Renderer 类别:

• faces-config.xml

....

<faces-config>

<render-kit>

<renderer>

<component-family>

javax.faces.Input </component-family>

<renderer-type>

onlyfun.caterpillar.TextCmd </renderer-type>

<renderer-class>

onlyfun.caterpillar.TextCmdRenderer </renderer-class>

</renderer>

</render-kit>

....

</faces-config>

为 Component 定义一个 Renderer,必须由 component family 与 renderer type 共同定义,

这并不难理解,因为一个 Component 可以搭配不同的 Renderer,但它是属于同一个 component family,例如 UIInput 就是属于 javax.faces.Input 这个组件家族,而我们为 它定义一个新的 Renderer。

接下未完成的范例可以取之前主题介绍过的,我们虽然没有自订组件,但我们为 UIInput 置换了一个新的 Renderer,这个 Renderer 会在输入字段上加入一个按钮。

如果您坚持使用之前自订的 UITextWithCmd,则可以如下修改:

• UITextWithCmd.java

package onlyfun.caterpillar;

import javax.faces.component.UIInput;

public class UITextWithCmd extends UIInput {

public UITextWithCmd() {

setRendererType("onlyfun.caterpillar.TextCmd");

} }

我们只是单纯的继承 UIInput,然后使用 setRendererType()设定

"onlyfun.caterpillar.TextCmd",但并没有为组件加入什么行为,看来什么事都没有作,

但事实上这是因为继承了 UIInput,它为我们处理了大多数的细节。

接下来同样的,设定自订 Tag:

• TextWithCmdTag.java

package onlyfun.caterpillar;

import javax.faces.application.Application;

import javax.faces.component.UIComponent;

import javax.faces.context.FacesContext;

import javax.faces.el.ValueBinding;

import javax.faces.webapp.UIComponentTag;

public class TextWithCmdTag extends UIComponentTag { private String size;

private String value;

public String getComponentType() {

return "onlyfun.caterpillar.TextWithCmd";

}

public String getRendererType() {

return "onlyfun.caterpillar.TextCmd";

} ...

}

要使用自订的 Component,记得要在 faces-config.xml 中再加入:

....

<component>

<component-type>

onlyfun.caterpillar.TextWithCmd </component-type>

<component-class>

onlyfun.caterpillar.UITextWithCmd </component-class>

</component>

...

4 参考数据

Core JavaServer Faces

使用 JavaServer Faces 开发 UI A first look at JavaServer Faces

Using JavaServer Faces Technology in JSP Pages Free JSF (JavaServer Faces) Training Materials Craig McClanahan's Weblog

JSF Central

Java Server Faces Resources

在文檔中 语言技术: JavaServer Faces (頁 91-105)

相關文件