views:

2399

answers:

2

Hello,

When I include a 'disabled' attribute on an a4j:commandButton, the button's action is not performed. Taking the 'disabled' attribute out causes it to work properly. I am not doing any special validation (that I'm aware of) and am not seeing any validation error messages.

Here is part of my page:

<t:dataTable id="myTable"
             var="region"
             value="#{MyPageBackingBean.regions}"
             width="100%">

...

<a4j:commandButton value="Update"
                   action="#{region.doUpdate}"
                   oncomplete="alert('done');"
                   disabled="#{!empty region && region.messageEmpty}"
                   immediate="true"/>

...

</t:dataTable>

Any ideas? Thanks!

Edit:

I tried setting preserveDataModel="true" on the t:dataTable to no avail.

I also made a test having an a4j:commandButton and text box with no data table, but the backing bean action is still not being fired:

      <h:form>
     <a4j:region>
        <a4j:outputPanel id="testregion">
        <h:messages id="messages"/>

                          <a4j:status>
                             <f:facet name="start">
                                <h:graphicImage value="/images/progress_indicator.gif"/>
                             </f:facet>
                          </a4j:status>

                       <h:inputTextarea
                             rows="5"
                             value="#{MyPageBackingBean.myValue}"
                             style="width:100%; border: 1px solid #99CCFF;">
                          <a4j:support event="onkeyup"
                                       reRender="testregion"
                                       eventsQueue="messageModificationQueue"
                                       ignoreDupResponses="true"
                                       requestDelay="500"/>
                       </h:inputTextarea>

                       <a4j:commandButton id="doDelete"
                                          value="Delete"
                                          action="#{MyPageBackingBean.dummy}"
                                          reRender="testregion"
                                          disabled="#{empty MyPageBackingBean.myValue}"/>
                    <h:outputText value="#{MyPageBackingBean.myValue}"/>
        </a4j:outputPanel>
     </a4j:region>
  </h:form>

Here is the new backing bean code used for testing:

private String m_myValue = null;
   public String getMyValue()
   {
      return m_myValue;
   }
   public void setMyValue(String value)
   {
      m_myValue = value;
   }
   private String mystr2 = null;
   public String dummy()
   {
      mystr2 = "hello";
      return null;
   }

Thanks!

+2  A: 

In HTML world, the disabled attribute causes the name-value attribute pair of any HTML input element (input, select, textarea and button) not being sent to the server side.

In JSF world, the presense of the name attribute is been used to identify the bean action to be invoked at the server side. However, during apply request values phase of the form submit JSF also checks if the component's disabled (and rendered) attribtue evaluates true before taking any actions.

The #{region} is here an iterated table row object of #{MyPageBackingBean.regions} whose isMessageEmpty() getter by default returns true. In the new request of the form submit the #{MyPageBackingBean.regions} is apparently empty which effectlively makes the button disabled. JSF won't invoke the associated action then.

To the point, you need to make sure that #{MyPageBackingBean.regions} returns exactly the same datamodel in the subsequent request. Easiest fix is to place the MyPageBackingBean bean in session scope so that it don't get reinitialized in the subsequent request, but that has more negative side effects as well. Another fix is to rearrange the datamodel loading so that it happens in the bean constructor. As you're already using Tomahawk's <t:dataTable>, you need to set its preserveDataModel attribute to true. For more hints about using datatables, you may find this article useful as well: Using Datatables.

BalusC
Thanks for the info, I'll definitely try that out. I bet part of my problem could be that MyPageBackingBean is request-scoped. Maybe preserveDataModel will help.
Jon
No such luck with preserveDataModel, or moving the commandbutton and text box out of the datatable (see my edited question). The backing bean is request-scoped though, that might be the issue. Any thoughts?
Jon
Maybe it doesn't work well with ajaxical requests. I haven't done a4j extensively, so I can't guarantee the working, but in theory using `<a4j:keepAlive>` on the particular request scoped bean should fix this problem.
BalusC
I ended up making the backing bean session-scoped to get this to work; I tried using t:saveState on the backing bean (not a4j:keepAlive, that would have been interesting to try) when it was request-scoped, but that didn't help. "preserveDataModel" on the t:dataTable didn't help either but was worth a shot. Thanks!
Jon
+1  A: 

One other thing to note about the disabled property for an a4j:commandButton: if the disabled property is set to true, then the ajax hook for the button's onclick event is never rendered into the final HTML. That is, you get a button like this:

<input type="button" onclick="return false" ... />

instead of this:

<input type="button" onclick="A4J.AJAX.Submit('....');return false" ... />

So, if you want to do something like this:

  1. Render page with disabled a4j:button
  2. Enable a4j:button on client side based on user interaction
  3. Click on a4j:button to call server side action

Then you will instead need to do something like this:

  1. Render page with a4j:button and disabled="#{true}"
  2. Have the user interaction trigger a re-render of the a4j:button with disabled="#{false}"
  3. Click on the a4j:button to call the server side action

Here's a simple example:

<h:selectOneCheckbox value="#{myAction.booleanProperty}">
  <a4j:support event="onclick" reRender="button1" />
</h:selectOneCheckbox>
<a4j:commandButton id="button1" action="#{myAction.doSomething}" 
  disabled="#{myAction.booleanProperty eq false}" value="click me"
/>
drteeth
Interesting, thanks for the info!
Jon