1
votes

I have a ViewScoped ManagedBean. This bean has a boolean attribute which controls whether a datatable should be displayed. See below:

<p:dataTable value="#{loc.locationRows}" var="obj" ... rendered="#{loc.renderLocationTable}">   
    <p:column>
        ...
    </p:column>

    ...
</p:dataTable>

My ManagedBean looks like this:

@ManagedBean(name = "loc")
@ViewScoped
public class LocationController implements Serializable {
    private boolean renderLocationTable = false;

    // JSF ActionListener.
    public void methodA() {
        if(someCondition) {
            renderLocationTable = true; // this is the only time we should render location table
        }
    }
}

As soon as methodA() gets called and some condition is met, then the table should be rendered; and this works fine. But, the problem is this, for each and every other JSF ActionListener method which gets called, I have to explicitly set the rendered boolean back to false. See below:

@ManagedBean(name = "loc")
@ViewScoped
public class LocationController implements Serializable {
    private boolean renderLocationTable = false;

    // JSF ActionListener.
    public void methodA() {
        if(someCondition) {
            renderLocationTable = true; // this is the only time we should render location table
        }
    }        

    // JSF ActionListener.
    public void methodB() {
        renderLocationTable = false;
    }

    // JSF ActionListener.
    public void methodC() {
        renderLocationTable = false;
    }
}

I've given a very small snippet of the actual ManagedBean and XHTML file. In-reality, these files are huge and lot's of stuff is happening with several other boolean "rendered" flags. It is becoming increasingly difficult to keep these flags accurate. Plus, each ActionListener method now has to know about all boolean flags even if they are not related to the business at-hand.

This is what I'd love to be able to do:

<f:event type="postRenderView" listener="#{loc.resetRenderLocationTable}" />
<p:dataTable value="#{loc.locationRows}" var="obj" ... rendered="#{loc.renderLocationTable}">   
    <p:column>
        ...
    </p:column>

    ...
</p:dataTable>

Then, in the ManagedBean have a method:

public void resetRenderLocationTable(ComponentSystemEvent event) {
    renderLocationTable = false;
}

Wouldn't this be nice? No more playing games with resetting boolean variables. No more test cases where we need to make sure the table doesn't get displayed when it shouldn't be. The rendered flag can be set to true when the appropriate JSF ActionListener method sets it to true and then the "post-back" call will reset the flag back to false...Perfect. BUT, apparently there's no way of doing this out-of-the-box with JSF.

So, does anyone have a solution to this issue?

Thanks!

By the way, this situation happens probably a lot more than you think. Anytime you have a form with several commandButtons using ActionListeners, then this situation could happen to you. If you've ever had a JSF ManagedBean and you find yourself setting boolean flags to true or false scattered through-out the class, then this situation applies to you.

1
If all that action listeners are doing is to reset that flag, then you can point action listeners for various components to the same method in the managed bean, isn't it? So there will be only one place where you reset that value.Bogdan
Why can't you move the logic for checking someCondition into preRenderView event listener? The listener should toggle the state of the variable on every postbackkolossus
The other ActionListener methods (methodB() and methodC() as shown above) do much more than reset that flag. In-fact, methodB() and methodC() have nothing to do with that flag except that if they don't set that flag equal to false then the datatable will be displayed when it shouldn't be.David Jensen
Also, the reason why preRenderView wouldn't be effective is, imagine methodA() as a very complex and resource-intensive method. PreRenderView would execute that code each and every time a postback happened. The way I've got it, methodA() only gets called when it needs to.David Jensen
So for example, when I say: if(someCondition) { renderLocationTable = true }; The amount of work required to determine "someCondition" is high. This is why I only want to execute that code when it needs to be.David Jensen

1 Answers

0
votes

You didn't added primefaces tag, but according to your code I see that you are using Primefaces. A suppose your methodA() is called from, for example p:commandButton. I suggest first to create primefaces remote command:

<p:remoteCommand name="resetRenderLocationTable">
  <f:setPropertyActionListener value="#{false}" target="#{loc.renderLocationTable}"/>
</p:remoteCommand>

this will create JavaScript function named resetRenderLocationTable whose call will generate AJAX request which will set renderLocationTable property to false. Now just add call to that function in oncomplete of you commandButton (or any other AJAX source):

<p:commandButton action="#{loc.methodA()}" update="myDatatable" oncomplete="resetRenderLocationTable()"/>

In next request you don't have to worry about resetting this property, just update your datatable.