Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you update an h:outputLabel from a p:ajax listener?

I'm trying to use a p:ajax tag and then in that listener, i'm setting a value called "periodRendered". then i'm trying to update an h:outputLabel tag via an update from the p:ajax tag. It's not updating ajaxily and i'm thinking it's because a primefaces ajax tag can't update a standard jsf outputLabel tag.

Is my assumption correct and is there a more appropriate tag i should be using instead of h:outputLabel ?

<h:outputLabel for="addProgramTo" value="Add Program To" />
<p:selectOneMenu value="#{ppBacker.grantProgram.grant_project_id}" id="addProgramTo" size="1" styleClass="listBoxMedium">
    <p:ajax process=":addProgram:addProgramTo" update=":addProgram:periodGrid, :addProgram:periodLabel" event="change" listener="#{ppBacker.addProgramListener}" />
    <f:selectItems value="#{ppBacker.grantProjectDropDownList}" />
</p:selectOneMenu>            

<h:outputLabel for="period" value="Period" id="periodLabel" rendered="#{ppBacker.periodRendered}"> 
like image 904
Catfish Avatar asked Apr 27 '12 21:04

Catfish


2 Answers

You cannot update elements which are not rendered , rendered=false "is a JSF way to" to remove elements from the DOM Tree ,

its not like css display:none or visibility:hidden <- this two will keep the elements in the DOM tree but hidden , while the JSF rendered=false wont even render (keep) the element in the DOM tree (you wont even see it in the "view source" of the page)

So in you case you need to wrap the outputLabel with `panelGroup' and update the id of the wrapper

<h:panelGroup id="periodLabelWrapper">
    <h:outputLabel for="period" value="Period" id="periodLabel" rendered="#{ppBacker.periodRendered}">
</h:panelGroup>

and refer to the wrapper (which always be in the DOM tree) id in the <p:ajax update attribute, like this:

<p:ajax process=":addProgram:addProgramTo" update=":addProgram:periodGrid, :addProgram:periodLabelWrapper" event="change" listener="#{ppBacker.addProgramListener}" />

Another solution would be the update the entire form like this <p:ajax update="@form" ... that way you don't need the wrap the outputLabel

regarding your comment question

how does @form update the un-rendered elements, but targeting them directly through id's does not?

You can't target an element in update which is not present in the page (rendered=false) "its not there"

But when you use update="@form" or update="someWrapperID" the form/wrapper "re evaluates" all its inner elements , including the ones with rendered="#{someBean.someCondition}" and the condition gets "re evaluated" so if it result to true the elemet will be displayed...

like image 184
Daniel Avatar answered Sep 30 '22 17:09

Daniel


I'm not sure the reason for this behavior, but I'm putting money on the fact that p:ajax is going to require an active target for the update to work. Here's an example of it working/not working:

<h:body styleClass="center">
    <f:view contentType="text/html">
        <h:form id="test">
            <h:outputLabel for="addProgramTo" value="Add Program To: " />
            <h:selectOneMenu id="addProgramTo" value="#{testScope.target}">
            <p:ajax update=":test:periodLabel, :test:wrapper" event="change" process="@form">
            </p:ajax>
            <f:selectItems value="#{testScope.values}"/>
            </h:selectOneMenu>            
            <hr />
            <p:outputPanel id="wrapper">
                <h:outputLabel value="Works, has a target" rendered="#{testScope.timeToRender}"/>
            </p:outputPanel>
            <hr />
            <h:outputLabel value="does not work" id="periodLabel" rendered="#{testScope.timeToRender}" />   
        </h:form>
    </f:view>

</h:body>

//Bean...

@ViewScoped
@ManagedBean(name = "testScope")
public class TestScope
{
    private String[] values = { "False", "What?", "True" };
    private String target = "Nothing";

    /**
     * @return the target
     */
    public String getTarget()
    {
        return target;
    }

    /**
     * @return the values
     */
    public String[] getValues()
    {
        System.out.println(values);
        return values;
    }

    public boolean isTimeToRender()
    {
        return "True".equals(target);
    }

    /**
     * @param target the target to set
     */
    public void setTarget(String target)
    {
        this.target = target;
    }
}

I don't have the time to dig into this one, but from a quick look (again QUICK) in the debugger it looks like it doesn't find the ID (it isn't rendered) so it doesn't get the update. Why? I'm not sure--the easy workaround is to place it inside something you can update (p:outputPanel) and render it as a span, then your code should work. Is this acceptable? Not really...

If you can deal with a full form update the following will work as well ->

<p:ajax update=":test" event="change" process="@form">

Since you're targeting the enclosing form it updates all the children. If I was doing this, that is the workaround I'd use. It isn't a lot of data and it is a heck of a lot cleaner. I'm not sure if this a bug or a feature. It feels buggy though.

Short answer: yes you can target a label. Long answer: you can't target one that isn't rendered. I really hope this helps.

like image 36
Daniel B. Chapman Avatar answered Sep 30 '22 16:09

Daniel B. Chapman