Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I pass an attribute from composite component to backing bean by using backing component?

I have the following code in my facelet page:

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

This is my composite component:

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

How do I pass attributes from, to and text from composite component to backing bean? I mean inject these values in backing component, and not through

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

Update: there's more correct definition what do I need: Be able to mutate objects which I pass from the backing bean to the composite component inside a backing component of the composite component. So when I perform process or execute my composite component I get the updated values.

This is my backing 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
    */

}

I just can't realize how do I move on? In general I need to take a value from <p:inputText id="hiddenValue" value="#{cc.attrs.text}"/> and convert it to two Calendars object from and to. It will be great if somebody can point me toward right direction from here. I know that I need to use getAttributes().put(key,value) but don't know where to put this code. Thank you in advance.

like image 837
Anatoly Avatar asked Dec 30 '14 13:12

Anatoly


1 Answers

I rewrote the code using BalusC tecnique (and without PrimeFaces):

the form:

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

the composite:

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

the backing component:

@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;
    }
}

and the managed bean:

@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;
    }
}

Note that managed bean only retains range property for get/set. From and to are gone, the backing component derives and rebuilds them itself.

like image 110
Michele Mariotti Avatar answered Oct 28 '22 20:10

Michele Mariotti