Как передать атрибут из составного компонента в компонент поддержки с помощью компонента поддержки?

у меня есть следующий код на моей странице facelet:

  <hc:rangeChooser1 id="range_chooser" 
                    from="#{testBean.from}"
                    to="#{testBean.to}"
                    listener="#{testBean.update}"
                    text="#{testBean.text}">
        <f:ajax event="rangeSelected"
                execute="@this"
                listener="#{testBean.update}"                   
                render=":form:growl range_chooser"/>
    </hc:rangeChooser1>

это мой составной компонент:

<ui:component xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:cc="http://java.sun.com/jsf/composite"
    xmlns:p="http://primefaces.org/ui">
    <cc:interface componentType="rangeChooser">
        <!-- Define component attributes here -->
        <cc:clientBehavior name="rangeSelected" event="change" targets="hiddenValue"/>
        <cc:attribute name="from" type="java.util.Calendar"/>
        <cc:attribute name="to" type="java.util.Calendar"/>
        <cc:attribute name="text" type="java.lang.String"/>

    </cc:interface>


    <cc:implementation>

        <div id="#{cc.clientId}">
                 ...
                <p:inputText id="hiddenValue" value="#{cc.attrs.text}"/>
                 ...
        </div>
    </cc:implementation>
</ui:component>

как передать атрибуты from, to и text от составного компонента к бэк-Бобу? Я имею в виду ввести эти значения в компонент поддержки, а не через

<p:inputText id="hiddenValue" value="#{cc.attrs.text}"/>

Update: есть более правильное определение, что мне нужно: иметь возможность мутировать объекты, которые я передаю из backing bean до composite component внутри a backing component на composite component. Поэтому, когда я выступаю process или execute мой composite component Я получаю обновленные значения.

это мой компонент поддержки:

@FacesComponent("rangeChooser")
public class RangeChooser extends UIInput implements NamingContainer  {
    private String text;
    private Calendar from;
    private Calendar to;

    @Override
    public void encodeBegin(FacesContext context) throws IOException{

        super.encodeBegin(context);
    }


    public String getText() {
        String text = (String)getStateHelper().get(PropertyKeys.text);
        return text;
    }

    public void setText(String text) {
        getStateHelper().put(PropertyKeys.text, text);
    }

    /*
        same getters and setters for Calendar objects, from and to
    */

}

я просто не могу понять, как мне двигаться дальше? В общем мне нужно взять значение из <p:inputText id="hiddenValue" value="#{cc.attrs.text}"/> и преобразовать его в два объекта календарей from и to. Было бы здорово, если бы кто-нибудь мог указать мне правильное направление отсюда. я знаю, что мне нужно использовать getAttributes().put(key,value) но не знаю, куда поместить этот код. заранее спасибо.

2 ответов


на основе ваших комментариев, это то, что вы ожидаете.

обратите внимание, что даже если эта реализация работает, это концептуально неверно!

вы рассматриваете from и to как входы (не входные компоненты, а входные значения) и text как выход. Это не то, как JSF предназначен для работы!

вот это
<cc:interface componentType="rangeComponent">
    <cc:attribute name="from" />
    <cc:attribute name="to" />
    <cc:clientBehavior name="rangeSelected" event="dateSelect" targets="from to"/>
</cc:interface>

<cc:implementation>

    <div id="#{cc.clientId}">
        <p:calendar id="from" value="#{cc.attrs.from}"/>
        <p:calendar id="to" value="#{cc.attrs.to}"/>
    </div>

</cc:implementation>

используется на странице:

<h:form>
    <e:inputRange from="#{rangeBean.from}" to="#{rangeBean.to}" text="#{rangeBean.text}">
        <p:ajax event="rangeSelected" process="@namingcontainer" update="@form:output" listener="#{rangeBean.onChange}" />
    </e:inputRange>

    <h:panelGrid id="output" columns="1">
        <h:outputText value="#{rangeBean.from}"/>
        <h:outputText value="#{rangeBean.to}"/>
        <h:outputText value="#{rangeBean.text}"/>
    </h:panelGrid>
</h:form>

С этой поддержкой компонент:

@FacesComponent("rangeComponent")
public class RangeComponent extends UINamingContainer
{
    @Override
    public void processUpdates(FacesContext context)
    {
        Objects.requireNonNull(context);

        if(!isRendered())
        {
            return;
        }

        super.processUpdates(context);

        try
        {
            Date from = (Date) getValueExpression("from").getValue(context.getELContext());
            Date to = (Date) getValueExpression("to").getValue(context.getELContext());

            ValueExpression ve = getValueExpression("text");
            if(ve != null)
            {
                ve.setValue(context.getELContext(), from + " - " + to);
            }
        }
        catch(RuntimeException e)
        {
            context.renderResponse();
            throw e;
        }
    }
}

С этой поддержкой bean:

@ManagedBean
@ViewScoped
public class RangeBean implements Serializable
{
    private static final long serialVersionUID = 1L;

    private Date from = new Date(1000000000);
    private Date to = new Date(2000000000);
    private String text = "range not set";

    public void onChange(SelectEvent event)
    {
        Messages.addGlobalInfo("[{0}] changed: [{1}]", event.getComponent().getId(), event.getObject());
    }

    // getters/setters
}

я переписал код с помощью BalusC tecnique (и без PrimeFaces):

форма:

<h:form>
    <e:inputRange value="#{rangeBean.range}">
        <p:ajax event="change" process="@namingcontainer" update="@form:output"
            listener="#{rangeBean.onChange}" />
    </e:inputRange>

    <h:panelGrid id="output" columns="1">
        <h:outputText value="#{rangeBean.range}" />
    </h:panelGrid>
</h:form>

композитный:

<cc:interface componentType="rangeComponent">
    <cc:attribute name="value" />
    <cc:clientBehavior name="change" event="change" targets="from to"/>
</cc:interface>

<cc:implementation>

    <div id="#{cc.clientId}">
        <h:inputText id="from" binding="#{cc.from}">
            <f:convertDateTime type="date" pattern="dd/MM/yyyy" />
        </h:inputText>
        <h:inputText id="to" binding="#{cc.to}">
            <f:convertDateTime type="date" pattern="dd/MM/yyyy" />
        </h:inputText>
    </div>

</cc:implementation>

поддержка компонента:

@FacesComponent("rangeComponent")
public class RangeComponent extends UIInput implements NamingContainer
{
    private UIInput from;
    private UIInput to;

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

    @Override
    public void encodeBegin(FacesContext context) throws IOException
    {
        String value = (String) getValue();
        if(value != null)
        {
            String fromString = StringUtils.substringBefore(value, "-");
            String toString = StringUtils.substringAfter(value, "-");

            try
            {
                from.setValue(from.getConverter().getAsObject(context, from, fromString));
            }
            catch(Exception e)
            {
                from.setValue(new Date());
            }

            try
            {
                to.setValue(to.getConverter().getAsObject(context, to, toString));
            }
            catch(Exception e)
            {
                to.setValue(new Date());
            }
        }

        super.encodeBegin(context);
    }

    @Override
    public Object getSubmittedValue()
    {
        return (from.isLocalValueSet() ? from.getValue() : from.getSubmittedValue()) + "-" + (to.isLocalValueSet() ? to.getValue() : to.getSubmittedValue());
    }

    @Override
    protected Object getConvertedValue(FacesContext context, Object submittedValue)
    {
        return from.getSubmittedValue() + "-" + to.getSubmittedValue();
    }

    public UIInput getFrom()
    {
        return from;
    }

    public void setFrom(UIInput from)
    {
        this.from = from;
    }

    public UIInput getTo()
    {
        return to;
    }

    public void setTo(UIInput to)
    {
        this.to = to;
    }
}

и управляемый компонент:

@ManagedBean
@ViewScoped
public class RangeBean implements Serializable
{
    private static final long serialVersionUID = 1L;

    private String range = "01/01/2015-31/12/2015";

    public void onChange(AjaxBehaviorEvent event)
    {
        Messages.addGlobalInfo("[{0}] changed: [{1}]", event.getComponent().getId(), event.getBehavior());
    }

    public String getRange()
    {
        return range;
    }

    public void setRange(String range)
    {
        this.range = range;
    }
}

обратите внимание, что управляемый компонент сохраняет только range свойство для get / set. From и to ушли, компонент поддержки выводит и перестраивает их сам.