组件卷标

完成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);

        setStringProperty(component, "size", size);
        setStringProperty(component, "value", value);
    }

    private void setStringProperty(UIComponent component,
                       String attrName, String attrValue) {
        if(attrValue == null)
            return;

        if(isValueReference(attrValue)) {
            FacesContext context =
                         FacesContext.getCurrentInstance();
            Application application =
                         context.getApplication();
            ValueBinding binding =
                   application.createValueBinding(attrValue);
            component.setValueBinding(attrName, binding);
        }
        else {
            component.getAttributes().
                          put(attrName, attrValue);
        }
    }

    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,所传回的值在faces-config.xml中要有定义,例如:

....
 <component>
    <component-type>
        onlyfun.caterpillar.TextWithCmd
    </component-type>
    <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属性:

UITextWithCmd.java
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 {
        ....

        String size = (String) getAttributes().get("size");
        if(size != null) {
            writer.writeAttribute("size", size, null);
        }
        .....
    }
    ....
 }

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

....
 private void setStringProperty(UIComponent component,
                        String attrName, String attrValue) {
        if(attrValue == null)
            return;

        if(isValueReference(attrValue)) {
            FacesContext context =
                          FacesContext.getCurrentInstance();
            Application application =
                          context.getApplication();
            ValueBinding binding =
                   application.createValueBinding(attrValue);
            component.setValueBinding(attrName, binding);
        }
        else {
            component.getAttributes().
                         put(attrName, 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-class>
     <body-content>empty</body-content>
     <attribute>
        <name>size</name>
     </attribute>
     <attribute>
        <name>value</name>
        <required>true</required>
     </attribute>
 </tag>

 </taglib>