I was checking out the code for this template from the OmniFaces Showcase app, when I encountered the following condition used in a p:selectOneMenu
:
disabled="#{facesContext.postback and not facesContext.renderResponse}"
From using the app, it seemed that the selectOneMenu is never disabled, so what does this code really do?
I know that facesContext.postback
is true
whenever the page is resultant from POST requests generated by JSF components (commandButton/commandLink, etc), but what would be the usual expected state of facesContext.renderResponse
when evaluated in the View?
UPDATE: oops, just now I saw the comment: They're disabled in other phases than render response, because they otherwise complain that the model value cannot be set even though there's no form.
So, I'm guessing the condition is evaluated several times during Faces lifecycle, and the component is disabled until the last state (renderResponse) is reached, when facesContext.renderResponse
evaluates to true
, the whole expression evaluates to false
and the component is then enabled. Is that about right?
Those <p:selectOneMenu>
components are actually abused to have a nice <div><ul><li>
dropdown menu with a minimum of code ;) Their values represent the current menu group and page which are by design read-only (they have no setter method). The navigation takes place by JavaScript window.location
which is handled during change event. They are not part of any form and do not participate in any form submit.
The disabled
attribute is theoretically not mandatory, but when a non-ajax JSF form elsewhere in the very same page is submitted synchronously, then PrimeFaces SelectOneMenuRenderer
will still try to decode()
it in its entirety even though it is not enclosed in any form at all. It would ultimately cause the following exception when the model value is to be updated because there's no setter for that value:
javax.el.PropertyNotWritableException: /WEB-INF/templates/showcase.xhtml @28,80 value="#{parent.children[0].viewId}": The class 'org.omnifaces.showcase.Page' does not have a writable property 'viewId'.
at com.sun.faces.facelets.el.TagValueExpression.setValue(TagValueExpression.java:136)
at javax.faces.component.UIInput.updateModel(UIInput.java:818)
at javax.faces.component.UIInput.processUpdates(UIInput.java:735)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1242)
at org.primefaces.component.panel.Panel.processUpdates(Panel.java:304)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1242)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1242)
at javax.faces.component.UIViewRoot.processUpdates(UIViewRoot.java:1231)
at com.sun.faces.lifecycle.UpdateModelValuesPhase.execute(UpdateModelValuesPhase.java:78)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
When the disabled
attribute evaluates true
during postback, then the SelectOneMenuRenderer
will skip the decode()
during apply request values phase and thus also skip the update of model value. However, if it evaluates true
as well during render response phase, then it becomes unselectable (and thus unusable). So, it should not evaluate true during render response phase. The expression
disabled="#{facesContext.postback and not facesContext.renderResponse}"
achieves exactly that. All with all, it's basically kind of a workaround for the odd behavior of SelectOneMenuRenderer
(for which I've not really investigated the underlying cause yet).
To test it yourself, pull the project, remove the disabled
attribute and invoke a synchronous submit in for example the <o:onloadScript>
showcase page.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With