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

☆樱花仙子☆ 提交于 2019-12-06 01:01:02

问题


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.


回答1:


Based on your comments this is what you expect.

Note that even if this implementation works, it is conceptually incorrect!

You are considering from and to as INPUTS (not input components, but input values) and text as OUTPUT. This is not the way JSF is intended to work!

However, here it is

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

used in page:

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

with this backing component:

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

with this backing 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
}



回答2:


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.



来源:https://stackoverflow.com/questions/27706635/how-do-i-pass-an-attribute-from-composite-component-to-backing-bean-by-using-bac

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!