Как расширить компонент UISelectOne для свойства перечисления, чтобы автоматически заполнять все значения перечисления в качестве выбранных элементов?

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

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

Итак, внутри компонента я могу получить тип перечисления и получить все значения перечисления, используя отражение. Итак, в основном мне нужно переопределить единственный метод validateValue(..) из UiSelectOne и поместить список UiSelectItems в качестве дочернего элемента (в так же, как это делается в Tomahawk, см. компонент SelectOneLanguage).

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

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

Спасибо.


person bitec    schedule 14.10.2011    source источник
comment
Что не так с <h:selectOneMenu>? JSF имеет встроенный конвертер enum. Или вы не хотите каждый раз указывать <f:selectItems>?   -  person BalusC    schedule 14.10.2011
comment
Хорошо, что лучше: <h:selectOneMenu value="#{polis.currency}" > <f:selectItems value="#{currency}" itemValue="#{s}" itemLabel="#{s.display}" var="s" /> </h:selectOneMenu> или <h:enumSelectOneMenu value="#{polis.currency}" />? Upd: правильно, мне нужно избегать всего selectItems персонала   -  person bitec    schedule 14.10.2011
comment
Я вижу, к чему ты клонишь. На самом деле это хорошая идея. теоретически простой составной компонент с вспомогательным компонентом должен работать, но я бы предпочел сначала сам поэкспериментировать с ним, чтобы увидеть, действительно ли он работает так, как я задумал. Я опубликую ответ, если у меня будет время поиграть с ним и подтвердится, что это действительно так работает.   -  person BalusC    schedule 14.10.2011
comment
Я слишком долго возился с композитом и попытался подставить туда p:column (просто расширить таким же образом, как описано выше). В конце концов я вернулся к базовым 1.2 фрагментам лицевых панелей, и это сработало отлично, так как не требовалось никаких UiNamingContainers, хотя я потерял наследство и должен был дублировать множество атрибуты в моем taglib.xml. Я мог бы снова попробовать составной элемент для вышеуказанной цели, но я не хочу иметь два пространства имен библиотеки тегов внутри своего приложения! Очень сложно вспомнить, какая библиотека (пользовательская или составная) содержит этот тег. Извините за много писем :(   -  person bitec    schedule 14.10.2011


Ответы (1)


Один из лучших (но не самый простой) способов расширить некоторые компоненты JSF и добавить поведение без потери атрибутов — использовать плагин PrimeFaces JSF. Некоторая информация об этом находится здесь: 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>  
person bitec    schedule 28.10.2011