3
votes

Consider the following jsf:

<h:panelGroup id="current" rendered="#{taskManager.currentTask != null}">
    <table>
        <tr>
            <td><h:outputText value="#{taskManager.currentTask.title}" /></td>
            <td>
                <h:form>
                    <rich:calendar id="start"
                        popup="true"
                        datePattern="yyyy-MM-dd HH:mm:ss"
                        showApplyButton="true" cellWidth="24px"
                        cellHeight="22px" style="width:200px"
                        enableManualInput="true"
                        value="#{taskManager.currentStart}">
                    </rich:calendar>

                    <h:commandButton value="Update">
                        <f:ajax execute="@form" render=":current" listener="#{taskManager.changeStart}"/>
                    </h:commandButton>

                    <h:commandButton value="Stop">
                        <f:ajax render=":current" listener="#{taskManager.stopCurrent}"/>
                    </h:commandButton>
                </h:form>
            </td>
        </tr>
        <tr>
            <td></td>
            <td>
                <h:form>
                    <rich:calendar id="stop"
                        popup="true"
                        datePattern="yyyy-MM-dd HH:mm:ss"
                        showApplyButton="true" cellWidth="24px"
                        cellHeight="22px" style="width:200px"
                        enableManualInput="true"
                        value="#{taskManager.currentStop}">
                    </rich:calendar>

                    <h:commandButton value="Stop At">
                        <f:ajax execute="@form" render=":current" listener="#{taskManager.stopCurrentAtSpecified}"/>
                    </h:commandButton>
                </h:form>
            </td>
        </tr>
    </table>
</h:panelGroup>

As I gather from the documentation, the "listener" in the f:ajax tag should be executed before the partial view ":current" is rerendered. This is based on:

  • docs.oracle.com/javaee/6/tutorial/doc/gkace.html : The listener's processAjaxBehavior method is called once during the Invoke Application phase of the lifecycle
  • javaserverfaces.java.net/nonav/docs/2.0/pdldocs/facelets/ : Identifiers of components that will participate in the "render" portion of the Request Processing Lifecycle
  • www.ibm.com/developerworks/library/j-jsf2/ : render is indeed after invoke...

The method "#{taskManager.stopCurrentAtSpecified}" will stop the task at the given time and set it to "null", this means on rerender the evaluation "#{taskManager.currentTask != null}" is no longer true so the grid panel should disappear. This is not the case. The task is indeed stopped, but the rerendered view still shows the old task. A refresh of the entire page will render it correctly.

Additionally I played around with the "#{taskManager.changeStart}", it would update the "#{taskManager.currentStop}" to a very large date so you would suspect if the listener was executed and the render performed after, that the calendar would reflect this large date, it does not.

I found someone with a similar problem, but his question was left unanswered: f:ajax with listener and render

I am using Mojarra 2.1.2

1

1 Answers

1
votes

Can you try to add another panelGroup which is always visible and rerender that? Something like

<h:panelGroup id="current">
  <h:panelGroup rendered="#{taskManager.currentTask != null}">
    ...
  </h:panelGroup>
</h:panelGroup>

Partial updates replace content of element with indicated id. I suspect it could be problematic when updated element itself is not rendered. I expect it to be skipped in the rendering phase and no update generated as a consequence.

Use ui:fragment or ui:component instead of nested h:panelGroup if you want to avoid an extra span.