Search code examples
jsf-2richfaces

a4j:commandButton onbegin event firing on form load


I'm having a problem with a4j:commandButton. What I'm trying to accomplish is this:

I have form with a submit and cancel button. When the user clicks the submit button, I want both buttons to be disabled, and a popup that asks the user to confirm. Once the user confirms, if there are validation errors, I want the submit and cancel buttons to be re-enabled. I've tried many solutions, but none seem to work. If you have a better solution than the one below, please do post. My question pertains though to the code below

                        <h:panelGrid id="submitPanel" columns="2">
                        <a4j:commandButton id="addRequestButton" render="addRequestButton,cancelButton" onbegin="#{addRequestBean.disableSubmitButton()}" styleClass="LoginButtonStyle" value="Submit" 
                                            disabled="#{addRequestBean.submitEnabled}" oncomplete="#{addRequestBean.enableSubmitButton()}"> 
                            <rich:componentControl target="popup" operation="show" />
                        </a4j:commandButton>
                        <h:commandButton  id="cancelButton" styleClass="LoginButtonStyle" value="Cancel" action="main" disabled="#{addRequestBean.submitEnabled}"> 
                        </h:commandButton>
                    </h:panelGrid>
                    <rich:popupPanel id="popup" modal="true" autosized="true" resizeable="false">
                        <f:facet name="header">
                            <h:outputText value="Verify"></h:outputText>
                        </f:facet>
                        <h:panelGrid id="popupgrid" columns="1">
                            <h:outputText styleClass="labelFontStyle" escape="false" value="All changes are final. &lt;br /&gt;Do you wish to continue?"></h:outputText>
                            <h:outputLabel styleClass="labelFontStyle" value="" for="">  
                            </h:outputLabel>
                        </h:panelGrid>
                        <h:panelGrid columns="2">
                            <h:commandButton  styleClass="LoginButtonStyle" value="Ok"  action="#{addRequestBean.saveRequest}"  onclick="#{rich:component('popup')}.hide()"> 
                            </h:commandButton>
                            <h:commandButton  styleClass="LoginButtonStyle" value="Cancel" onclick="#{rich:component('popup')}.hide()"> 
                            </h:commandButton>
                        </h:panelGrid>
                    </rich:popupPanel>

ViewScoped Bean.

I want the onbegin event to set a backing bean variable to disabled, and the oncomplete method to re-enable it. trouble is the onbegin event fires on form load, therefore disabling both the Submit and Cancel buttons. There must be something I'm doing wrong, if not, is there a workaround to this? Again if there is a better solution than this please do post.

Thanks


Solution

  • While I don't see the need to disable both buttons after displaying a modal popup (a modal popup by definition means that no other action will be possible on that page until the popup is closed or the page is refreshed), and you also haven't stated whether you want the submit button disabled by default(which is what your code indicates anyway), you may be able to achieve it by

    1. Wrapping both buttons in an <a4j:outputPanel/>. This way, both buttons are reRendered by any ajax action on that page anyway. Also what is action="main" doing on the cancel button? That will not work. Also, your choice of an <h:commandButton/> there means that it won't fire an ajax request to update anything in the view

      <a4j:outputPanel ajaxRendered="true" layout="block">
      <a4j:commandButton id="addRequestButton" styleClass="LoginButtonStyle" action = "#{addRequestBean.toggleSubmitButtonState}"  value="Submit" disabled="#{addRequestBean.submitEnabled}" oncomplete="#{rich:component('popup')}.show()"/> 
      <h:commandButton id="cancelButton" styleClass="LoginButtonStyle" value="Cancel" action="main" disabled="#{addRequestBean.submitEnabled}"/> 
      </a4j:outputPanel>
      
    2. Add a method expression for your button that toggles the button state

        public void toggleSubmitButtonState(){
          this.submitEnabled = !submitEnabled;     
      
        }
      

      You really shouldn't bind the state of both buttons to the same boolean because semantically, they're doing different things but I'll stick to what you already have here

    3. I'd advise you take that popup out of the form that it's currently sharing with all the other components but again, let's work with what you have. Again, your choice of the <h:commandButton/> alone here will make sure you're not doing any ajax. Either add the <f:ajax/> or convert those buttons to <a4j:commandButton/>

        <h:commandButton  styleClass="LoginButtonStyle" value="Ok"  action="#{addRequestBean.saveRequest}"  oncomplete="#{rich:component('popup')}.hide()"/>
        <h:commandButton  styleClass="LoginButtonStyle" value="Cancel" onclick="#{rich:component('popup')}.hide()"/> <!-- This won't work as is, it will refresh the entire page instead of just firing the javascript event. Change to a4j-->
      
    4. Add a <f:event/> event handler to that page to check for validation failure.

      and in your backing bean, we add the method to check for validation failures

      public void checkValidationFailures(){
      
      FacesContext context = FacesContext.getCurrentInstance();
      if (context.isPostback() && context.isValidationFailed()){ //if the request is a postback and validation failed
      
         this.submitEnabled = true;             
      
        }
      }
      

      This will only work if you're throwing a ValidatorException within the saveRequest method. Otherwise, you're going to have to handle the failure yourself within the method and explicitly set the submitEnabled value to true and then reRender the button. This is why you probably should convert those buttons in the popup to a4j.