Как расширить компонент UISelectOne для свойства enum, чтобы автоматически заполнять все значения enum как выбранные элементы? - PullRequest
1 голос
/ 14 октября 2011

Я хочу использовать упрощенный тег select one, который автоматически генерирует список элементов для перечислений.Итак, результат будет:

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

Итак, внутри компонента я могу получить тип enum и получить все значения enum с помощью отражения.Итак, в основном мне нужно переопределить только один метод validateValue (..) из UiSelectOne и поместить список UiSelectItems в качестве дочернего элемента (таким же образомкак это делается в Томагавк , см. компонент SelectOneLanguage ).

Но что еще нужно сделать?Мне нужно описать атрибуты тега в моем собственном taglib.xml, но jsf-impl.jar не содержит xml-файлов facelets - только файл taglib, поэтому я не могу просто скопировать все оттуда.Кроме того, если я статически опишу тег в моем файле taglib.xml - мне придется обновить его вручную в каждой новой версии JSF, что совсем нехорошо.Итак, каков наилучший способ расширить компонент в JSF и избежать большого количества ручной работы по копированию и вставке?

Ps Я использую JSF 2.0, но способ с использованием композитных фейслетов мне не подходит, так как он производитмного проблем, поскольку составной элемент обернут компонентом NamingContainer.Поэтому мне нужен только "oldschool" способ создания пользовательских компонентов.

Спасибо.

1 Ответ

0 голосов
/ 28 октября 2011

Один из лучших (но не самых простых) способов расширения некоторых компонентов JSF и добавления поведения без потери атрибутов - использование плагина JSF PrimeFaces.Некоторая информация об этом находится здесь: http://code.google.com/p/primefaces/wiki/BuildingFromSource. Хотя он имеет некоторые жестко закодированные значения (имя лицевой стороны 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. taglib.xml со ВСЕМ стандартным html выберите один атрибут меню
  2. Класс компонента, который содержит пользовательские логики для рендеринга clidhren:

    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>  
...