JSF ValueChangeListener не вызывается для композита с вспомогательным компонентом

Я пытаюсь зафиксировать старое/новое значение для проверки бизнеса. Для этого ValueChangeListener показался хорошим выбором. Он отлично работал в h: selectOneMenu, однако он не вызывается при использовании с домашним составным компонентом с вспомогательным компонентом. Любая идея, что я делаю неправильно?

Стоит добавить, что при удалении атрибута componentType из state.xhtml valueChangeListener работает, как и ожидалось...

Компонент:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"  
    xmlns:composite="http://java.sun.com/jsf/composite">

    <composite:interface displayName="state" componentType="com.company.dept.system.ui.address.State" shortDescription="State Information Display/Input Component">
        <composite:attribute name="value" type="java.lang.String" required="true" shortDescription="The value of the component" />      
        <composite:editableValueHolder name="state" />              
    </composite:interface>

    <composite:implementation>
    <div id="#{cc.clientId}">

            <h:selectOneMenu id="state" value="#{cc.attrs.value}">
                <f:selectItem itemLabel="(select)" noSelectionOption="true"/>
                <f:selectItems var="item" itemLabel="#{item.displayValue}" value="#{cc.states}" />
            </h:selectOneMenu>      

    </div>

    </composite:implementation>
</html>

Компонент поддержки

@FacesComponent("com.company.dept.system.ui.address.State")
public class State extends UIInput implements NamingContainer {

    private List<com.company.dept.policy.enums.State> states;

    @Override
    public String getFamily() {
        return UINamingContainer.COMPONENT_FAMILY;
    }

    /**
     * Prepare the list of states to display
     */
    public List<com.company.dept.policy.enums.State> getStates(){

        if (states != null) {
            return states;
        }

        states = new ArrayList<com.company.dept.policy.enums.State>();              
        for (com.company.dept.policy.enums.State st : com.company.dept.policy.enums.State.values()) {
            if(!st.equals(com.company.dept.policy.enums.State.NWNORWAY) && !st.equals(com.company.dept.policy.enums.State.UNKNOWN) &&  !st.equals(com.company.dept.policy.enums.State.TTTRUST_TERRITORY_AND_GUAM)) {            
                states.add(st);
            }
        }
        Collections.sort(states,new StateNameComparator());

        return states;
    }

}

Слушатель изменения значения

public class ClientValueChangeListener implements ValueChangeListener {

    @Override
    public void processValueChange(ValueChangeEvent event)
            throws AbortProcessingException {
        System.out.println("*****************************");
        System.out.println("VALUE CHANGE LISTENER. OLD=" + event.getOldValue() + " - NEW=" + event.getNewValue());
        System.out.println("*****************************");
         }
}

потребляющая страница:

<h:form>
    <address:state value="#{testPage.state}">
        <f:valueChangeListener type="com.company.dept.system.ui.clientinformation.ClientValueChangeListener" for="state"/>
    </address:state>
    <h:commandButton id="submitButton" value="Test" action="#{testPage.act}"/>          
</h:form>

person Ali Cheaito    schedule 18.06.2013    source источник
comment
Какая реализация/версия JSF? Что, если вы явно укажете <cc:editableValueHolder targets>?   -  person BalusC    schedule 18.06.2013
comment
Использование Mojarra 2.1.13 на jBoss EAP 6. Я попытался установить target=state для editableValueHolder, но это не дало никакого эффекта.   -  person Ali Cheaito    schedule 18.06.2013
comment
Теперь я вижу, что ваш вспомогательный компонент простирается от UIInput. Я бы сказал, просто избавься от него. Я разместил ответ.   -  person BalusC    schedule 18.06.2013


Ответы (1)


Это потому, что ваш вспомогательный компонент простирается от UIInput. Слушатель изменения значения применяется к самому вспомогательному компоненту, а не к дочернему компоненту составной реализации.

Ваше конкретное функциональное требование не совсем ясно, но на основе предоставленной информации вы можете безопасно заменить extends UIInput implements NamingContainer на extends UINamingContainer (и избавиться от переопределения getFamily()).

Если вы действительно хотите, чтобы ваш вспомогательный компонент расширялся от UIInput, то вам следует делегировать отправленное значение, локальное значение и значение вспомогательного компонента дочернему раскрывающемуся компоненту, но это делает дизайн технически мало или бессмысленным.

person BalusC    schedule 18.06.2013
comment
Вот в чем проблема! Вместо этого расширил UINamingContainer, и слушатель заработал! Спасибо! Кстати, бизнес-требование состоит в том, чтобы иметь раскрывающийся список состояний с предварительно упакованным внешним видом. - person Ali Cheaito; 18.06.2013
comment
Пожалуйста. Если вы когда-либо намеревались использовать несколько компонентов ввода в своем композите и намерены получить/установить его с помощью свойства одиночного значения, тогда вам действительно следует позволить своему вспомогательному компоненту расширяться от UIInput , но это требует, очевидно, немного больше работы. Но обратите внимание, что в таком случае было бы мало смысла нацеливать конкретный дочерний компонент ввода с прослушивателем изменения значения. - person BalusC; 18.06.2013
comment
У меня есть другие компоненты, которые расширяют UIInput и переопределяют encodeBegin, getSubmittedValue и getConvertedValue. Было проделано много работы, чтобы собрать их вместе, и я до сих пор не до конца понимаю, что делают все методы и какие другие методы доступны. Можете ли вы порекомендовать расширенный учебник, в котором объясняются вспомогательные компоненты? - person Ali Cheaito; 18.06.2013
comment
Возможно, полезно увидеть один разумный вариант использования в реальном мире: balusc.blogspot.com/2013/01/ Если ваш композит не содержит нескольких входных компонентов, значение которых должно быть получено/установлено из одного свойства bean-компонента, то, скорее всего, вам вообще не нужен этот подход. - person BalusC; 18.06.2013