如何为枚举属性扩展UISelectOne组件以自动将所有枚举值填充为选择项?

时间:2011-10-14 16:37:48

标签: jsf jsf-2 selectonemenu custom-tag

我想使用简化的select one标签,它会自动为枚举生成选择项列表。所以,结果将是:

<s:enumSelectOneMenu value="#{myBean.enumValue}"/>

因此,在组件内部,我可以获取枚举类型并使用反射获取所有枚举值。所以,基本上我需要覆盖 UiSelectOne 中唯一一个方法 validateValue(..),并将 UiSelectItems 列表作为孩子放在那里(在与 Tomahawk 中的方式相同,请参阅 SelectOneLanguage 组件。

但还有什么应该做的?我需要在我自己的taglib.xml中描述标记属性,但是jsf-impl.jar不包含facelets xml - 只有taglib文件,所以我不能简单地从那里复制所有内容。另外,如果我在taglib.xml中静态描述标记 - 我将不得不在每个新版本的JSF上手动更新,这根本不是很好。那么,这是在JSF中扩展组件的最佳方法,并避免大量的手动复制粘贴工作?

P.S。我正在使用JSF 2.0,但是复合facelets方式不适合我,因为它会产生很多问题,因为复合元素由NamingContainer组件包装。所以我只需要“oldschool”方式来创建自定义组件。

感谢。

1 个答案:

答案 0 :(得分:0)

扩展一些JSF组件并添加行为而不丢失属性的最佳(但不是最简单)方法之一是使用PrimeFaces JSF插件。有关它的一些信息在这里:http://code.google.com/p/primefaces/wiki/BuildingFromSource。虽然它有一些硬编码值(facelets的名称taglib和输出目录的方式,生成的taglib放在哪里),并且可以在本地更改和重建 以下是PF jsf插件的示例

   <!-- Primefaces maven plugin -->
            <plugin>
                <groupId>org.primefaces</groupId>
                <artifactId>maven-jsf-plugin</artifactId>
                <version>1.2.1-SNAPSHOT</version>
                <executions>
                    <execution>
                        <id>generate-ui</id>
                        <phase>generate-sources</phase>
                        <configuration>
                            <uri>http://www.mycompany.com/tags</uri>
                            <name>rstk-tag</name>
                            <jsfVersion>2</jsfVersion>
                            <templatesDir>src/main/java-templates</templatesDir>
                            <componentConfigsDir>src/main/resources-maven-jsf/ui</componentConfigsDir>
                            <!-- <standardFacesConfig>src/main/resources-maven-jsf/standard-faces-config.xml</standardFacesConfig> -->
<!-- These are new attributes added manually to plugin source code! -->
                            <standardFaceletsTaglib>src/main/resources-maven-jsf/standard-facelets-taglib.xml</standardFaceletsTaglib>
                            <faceletsOutputDirectory>target/generated-sources/maven-jsf-plugin/META-INF</faceletsOutputDirectory>
                        </configuration>
                        <goals>
                            <goal>generate-components</goal>
                            <goal>generate-facelets-taglib</goal>
                        </goals>
                    </execution>                    
                </executions>
            </plugin>

在此之后,可以使用以下元数据xml进行生成:

 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE component SYSTEM "../misc/component.dtd" [
<!ENTITY standard_uicomponent_attributes        SYSTEM "../entities/standard_uicomponent_attributes.xml">
<!ENTITY output_component_attributes            SYSTEM "../entities/output_component_attributes.xml">
<!ENTITY input_component_attributes             SYSTEM "../entities/input_component_attributes.xml">
]>
<component>
    <tag>enumSelectOneMenu</tag>
    <tagClass>com.rstk.kasko.component.EnumSelectOneMenuTag</tagClass>
    <componentClass>com.rstk.kasko.component.EnumSelectOneMenu</componentClass>
    <componentType>com.rstk.kasko.component.EnumSelectOneMenu</componentType>
    <componentFamily>javax.faces.SelectOne</componentFamily>
    <rendererType>javax.faces.Menu</rendererType>
    <parent>javax.faces.component.html.HtmlSelectOneMenu</parent>
    <description>The tag for select one menu, which renders the enumerations list. No children necessary for this</description>
    <attributes>
        &input_component_attributes;
    </attributes>

</component>

这与EnumSelectOneMenuTemplate.java(模板,插入到生成的组件代码中)一起允许生成:

  1. 带有所有标准html的taglib.xml选择一个菜单属性
  2. 组件类,包含用于呈现clidhren的自定义逻辑:
  3. 
        public class EnumSelectOneMenu extends HtmlSelectOneMenu {
        ... // Generated staff here
        public boolean getRendersChildren() {
                return true;
            }
    
            /**
             * @see javax.faces.component.UIComponentBase#encodeBegin(javax.faces.context.FacesContext)
             */
            @Override
            public void encodeBegin(FacesContext context) throws IOException {
                super.encodeBegin(context);
    
                if (context.isPostback()) {
                    return;
                }
    
                UISelectItems selectItems = new UISelectItems();
                try {
                    selectItems.setValue(getEnumValuesList());
                } catch (Exception e) {
                    log.error("Failed to create enum list", e);
                }
                getChildren().add(selectItems);
    
            }
    
            /**
             * Creates the list of select items of format [ENUM, enum.getDisplay()] 
             * @return
             */
            private List getEnumValuesList() {
                List result = new ArrayList();
                ValueExpression ve = getValueExpression("value");
                Class enumClass = ve.getType(getFacesContext().getELContext());
                Method method = ReflectionUtils.findMethod(enumClass, "getDisplay", null);
                for (Object e : ve.getType(getFacesContext().getELContext()).getEnumConstants()) {
                    result.add(new SelectItem(e, (String) ReflectionUtils.invokeMethod(method, e)));
                }
                return result;
            }
        }
    

    然后这个组件可以用作简单的JSF选择一个组件(带有所有标准属性),但不需要每次都添加选择项,并允许任何其他子项放在那里:

    <s:enumSelectOneMenu value="#{polis.osMatrixType}" id="registryType">
            <p:ajax listener="#{osagoPolisBean.rollOsagoEndDate}" update="osagoUsagePeriod" process="osagoTable" event="change"/>                               
    </s:enumSelectOneMenu>