1 2 Previous Next 17 Replies Latest reply on Apr 20, 2010 9:49 AM by Wahid Atif

    Warn by leaving a form view with unsaved changes

    Wahid Atif Newbie

      Hello Richfaces users,

       

      maybe that's not the first time, that someone asks this question here, but unfortunately I couldn't found any concrete examples on the forum. So I'd like really to get help:

      I'd like to put some "input listeners" on my form views, so that when the user makes changes (typing in inputText, selection another value in selectMenu or checking a Checkbox), then he clicks on another Button / Link without saving, he will be warned by a dialog box remebering him to save the changes or leaving the view without saving (changes will be lost).

       

      I tried at first to put a boolean "inputChanged" on my Bean, then setting it to true with an adapted event (onchange) by each component. And by the buttons/links which link to another page, I tried to show a rich:modalpanel once the value of "inputChanged" is true but I couldn't realize this conditions.

       

      Thereafter I found a JS solution:

       

      <script language="javascript" type="text/javascript">
          // Keep flag when something had changed
          var changed = false;
      
          /*
          Save in "changed" flag, whether an input had been done by the user.
          after that the calling of this method will be removed
          from the corresponding attribute of the input elements,
          in order to avoid redundant method calls */
      
          function storeChange(aChanged, aElement) {
              changed = aChanged;
              aElement.setAttribute('onchange', '');
          }
          /*
          Finally and when the "changed" flag is true, a confirmation message will be shown
          to confirm whether the page could be left.   
          */
          window.onbeforeunload=function unloadAlert() {
              if(changed) {
                  
                  return 'You have unsaved changes on the page. If you click on OK, the changes will be lost';
              }
              
              return null;
          }
      </script>
      

       

      And in the view:

       

       

      And now I'd like rather than the JS terriying dialog box, a user-friendly rich:modalPanel which should do the same function.

       

      For any indication I will be very very thankful

        • 1. Re: Warn by leaving a form view with unsaved changes
          Wahid Atif Newbie

          Sorry I forgot to paste the other code:

           

          And in the view:
          
          
              <a4j:form>
                  <h:outputLabel id="label_name" value="Name" for="in_name" />
                  <h:inputText id="in_name" value="#{bean.name}"
                      onchange="storeChange(true, this)" />
                  <br />
          
                  <h:outputLabel id="label_email" value="E-Mail" for="in_email" />
                  <h:inputText id="in_email" value="#{bean.email}"
                      onchange="storeChange(true, this)" />
                  <br />
                  <a4j:commandButton oncomplete="javascript:changed=false"
                      value="Save" />
              </a4j:form>
          
          • 2. Re: Warn by leaving a form view with unsaved changes
            Harut Sargsyan Apprentice

            Hi, your solution is a little bit complicated in my oppinion.

            I would implement in this way:

             

            No mentioned javascript methods needed. You have to add action method for the buttons/links (which should redirect the page). Check in that method wether is there any changed values for the input components and initialize "inputChanged" boolean property. Something like this:

             

            public String redirectIfNoChangesAction() {

                 //Write here business logic identifying is there any changed value, and initialize "inputChanged" boolean property

                 .......................................


                 if(inputChanged) {

                      return "";

                 }


                 return "REDIRECT_TO_ANOTHER_PAGE";

            }

             

            And you should also add "oncomplete" for buttons/links:

            oncomplete="if (#{myBean.inputChanged}) Richfaces.showModalPanel('your_modal_panel')"

             

            Something like above functional.........

            • 3. Re: Warn by leaving a form view with unsaved changes
              Wahid Atif Newbie

              Hi Harut Sargsyan,

               

              first thanks for your interesting answer. I tried your suggestion on my  code, but was not able to find out how to identify through my bean  whether the values in input fields, comboboxes or checkboxes have been  changed during user input. How could you realise this?

               

              //Write here business logic identifying is there any changed  value, and initialize "inputChanged" boolean

              Thanks in advance

               

              Wahid

              • 4. Re: Warn by leaving a form view with unsaved changes
                Harut Sargsyan Apprentice

                Wahid, you have to somehow store initial values (when the page loads) of your input components in your bean class. There are several ways to do that...

                I can suggest you to have a such a Map in your bean class:

                 

                private Map<String, String> oldValues = new HashMap<String, String>();

                 

                This Map should have input component ID as a key and component initial value as value, and you have to initialize this map in the method you are initializing input components values:

                 

                public void methodThatYouUseForInitializingInputComponentsValues() {

                     // your logic

                     .....................

                     oldValues.put("in_name", name);

                     oldValues.put("in_email", email);

                // etc....

                }

                 

                After this you will have all initial values of components in the Map.

                So later you can easily define wether any value changed or not simply comparing properties of bean class (which corresponds to components values) with oldValues elements:

                 

                public String redirectIfNoChangesAction() {

                    if(!name.equals(oldValues.get("in_name")) || !email.equals(oldValues.get("in_email"))) {

                          inputChanged = true;

                     }


                     if(inputChanged) {

                          return "";

                     }


                     return "REDIRECT_TO_ANOTHER_PAGE";

                }

                1 of 1 people found this helpful
                • 5. Re: Warn by leaving a form view with unsaved changes
                  Wahid Atif Newbie

                  Hi Harut,

                   

                  thanks alot, I got now your suggestion to work correctly. But now I'm trying now to reach the following:

                  I've in my view several possibilities to be redirected to another page, so there's many buttons and also a rich:menuGroup in the top, so there's many possible ways so that the user will take to leave the page: How should I handle the redirectIfNoChanges() method to return the corresponding page name "REDIRECT_TO_ANOTHER_PAGE" avoiding to write more code in my bean ? I hope you  understand my question.

                  • 6. Re: Warn by leaving a form view with unsaved changes
                    Ilya Shaikovsky Master

                    you could pass parameters using f:param or a4j:actionparam with the outcomes for the particular control.

                    • 7. Re: Warn by leaving a form view with unsaved changes
                      Harut Sargsyan Apprentice

                      Hi Wahid, I can suggest you to have a private method in your bean class which should define wether any value changed or not:

                       

                      /**

                      * Here we have a functional which is in previous "redirectIfNoChangesAction()" method

                      */

                      private boolean isValueChanged() {

                            if(!name.equals(oldValues.get("in_name")) || !email.equals(oldValues.get("in_email"))) {

                               return true;

                           }

                          

                           return false;

                      }

                       

                      After this you can define as many action methods as much links/buttons you have, and return there corresponding "navigation key" for right page redirection:

                       

                      public void actionMethod1() {

                           if(isValueChanged()) {         

                                inputChanged = true;

                                return "";

                           }

                          

                           return "REDIRECT_TO_PAGE1";

                      }


                      public void actionMethod2() {

                           if(isValueChanged()) {

                                inputChanged = true;

                                return "";

                           }

                       

                           return "REDIRECT_TO_PAGE2";

                      }

                       

                      and so on......

                       

                      Also I agree with Ilya's suggestion... Chose what you consider for you preferable.

                      • 8. Re: Warn by leaving a form view with unsaved changes
                        Wahid Atif Newbie

                        Thanks again for you both. I still have some difficulties to realise Ilya's suggestion from one side, and to implement this concept for my all views from the other side.

                        1. So firstly when I'm doing:


                        public void setTargetPage(String targetPage) {
                                this.targetPage = targetPage;
                            }
                        
                        
                            public String getTargetPage() {
                                if(!name.equals(oldValues.get("in_name") || !email.equals(oldValues.get("in_email"))){
                                    inputChanged = true;
                                }else {
                                    inputChanged = false;
                                }       
                                if(inputChanged){
                                    return "";
                                }
                                return targetPage;   
                            } 
                        
                        

                        and in my jsp view:

                         

                        <a4j:commandButton value="Go to user list"
                                             oncomplete="if(#{myBean.inputChanged})Richfaces.showModalPanel('modalPanel')">
                                         <a4j:actionparam name="targetPage" value="userlist"  assignTo="#{myBean.targetPage}"></a4j:actionparam>
                        </a4j:commandButton>
                        

                         

                         

                        I could not be redirected to "userlist", nothing happens.

                         

                        2. And secondly I'm including a standard menu in each view. This standard menu was made in a seperate jsp view. Now I wonder how can I verify each time and on each view the inputs for changes, before to allow the user to navigate through the menu items into another view?

                        • 9. Re: Warn by leaving a form view with unsaved changes
                          Harut Sargsyan Apprentice

                          No, you are going by WRONG way.... And it seems you have missunderstanding with Ilya's suggestion. Ilya suggested to have actionparam just for identifying in the action method from what link/button action has been fired, so after it you will understand what page should be opened after the action.

                          In my suggested way there is no need for any a4j:actionparam/f:param. You have to just create separate JSF action methods for each link/button, and in that methods you can easely define wether any value changed or not by private method (isValueChanged() which we need to use in all actions). So in your provided code it shoud be:

                           

                          <a4j:commandButton value="Go to user list"
                                               oncomplete="if(#{myBean.inputChanged})Richfaces.showModalPanel('modalPanel')">
                                           <a4j:actionparam name="targetPage" value="userlist"  assignTo="#{myBean.targetPage}"></a4j:actionparam>
                          </a4j:commandButton> 

                           

                          Change above code to:

                          <a4j:commandButton value="Go to user list"
                                             action="#{myBean.
                          actionMethod1
                          }"
                                             oncomplete="if(#{myBean.inputChanged})Richfaces.showModalPanel('modalPanel')"/>


                          Where actionMethod1 is the method I posted previously.
                          B.t.w in my previous post return type of the action methods are void -- it is wrong-- it shoud
                          be String...

                          Action methods are returning JSF navigation String. So further navigation is done by corresponding
                          navigation rule (of returned String) in faces-config.xml.


                          I could not be redirected to "userlist", nothing happens.

                          Because you have no Action method in your posted code. And you have needed logic in the "getter" method
                          of "targetPage" property...
                          B.t.w. never write business logic in getter/setter method, instead
                          write them in action/actionListener methods...
                          • 10. Re: Warn by leaving a form view with unsaved changes
                            Wahid Atif Newbie

                            Thanks Harut again for your very helpful posts, at least I got your solution working. Now I'm trying to implement the following inside the rich:modalPanel:

                            When the user confirms leaving the form view with changes, he should will be redirected to the original view, on which he clicked on. And by cancelling, the rich:modalPanel has to get closed, and no redirecting should happen. How should I best realise this ?

                            • 11. Re: Warn by leaving a form view with unsaved changes
                              Harut Sargsyan Apprentice

                              Hi Wahid, again you need to write jsf action method for the "OK" button of modalpanel.

                              First of all declare String property in your bean class which will contain the navigation key of the last action method:

                               

                              private String navigateFromModal;

                               

                              and initialize this property in your action methods which are fired when buttons/links are clicked. In my previous example it should be:

                               

                              public String actionMethod1() {

                                   if(isValueChanged()) {         

                                        inputChanged = true;        

                                        navigateFromModal = "REDIRECT_TO_PAGE1";


                                        return "";

                                   }

                                  

                                   return "REDIRECT_TO_PAGE1";

                              }

                               

                              After this you will have the needed navigation key (e.g. REDIRECT_TO_PAGE1) stored in the navigateFromModal property.

                              Add Action method for the modalpanel "OK" button, and just return there navigateFromModal property...

                               

                              For the "Cancel" button -- just implement modalpanel closing via javascript.

                              • 12. Re: Warn by leaving a form view with unsaved changes
                                Wahid Atif Newbie

                                Hi Harut, I tried your suggested solution but I each time an Exception, so I did in my Bean:

                                 

                                 

                                private String navigateFromModalPanel;
                                ...
                                getter() & setter()
                                ...
                                public String actionMethod1() {
                                
                                      if(isValueChanged()) {
                                          inputChanged = true; 
                                
                                          navigateFromModal = "REDIRECT_TO_PAGE1";
                                
                                          return "";
                                     }
                                     return "REDIRECT_TO_PAGE1";
                                }
                                

                                On my jsp:

                                 

                                   <rich:modalPanel id="inputChangedMP" width="350" height="150" resizeable="false">
                                            ...            
                                            <h:panelGrid columns="2" cellspacing="5">
                                                <h:commandButton id="confirmRedirectBtn" value="OK" styleClass="rich-button default"
                                                                   action="#{myBean.navigateFromModalPanel}"/>
                                                <h:commandButton value="Cancel" onclick="Richfaces.hideModalPanel('inputChangedMP')"/>
                                            </h:panelGrid>
                                            <rich:componentControl for="inputChangedMP" attachTo="confirmRedirectBtn" operation="hide"
                                                                   event="onclick" />
                                   </rich:modalPanel>
                                

                                 

                                But I got:

                                 

                                 

                                javax.faces.FacesException: Error calling action method of component with id j_id_jsp_1594877688_1:content:confirmRedirectButton
                                    at org.apache.myfaces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:69)
                                    at javax.faces.component.UICommand.broadcast(UICommand.java:121)
                                    at org.ajax4jsf.component.AjaxViewRoot.processEvents(AjaxViewRoot.java:321)
                                    at org.ajax4jsf.component.AjaxViewRoot.broadcastEvents(AjaxViewRoot.java:296)
                                    at org.ajax4jsf.component.AjaxViewRoot.processPhase(AjaxViewRoot.java:253)
                                    at org.ajax4jsf.component.AjaxViewRoot.processApplication(AjaxViewRoot.java:466)
                                    at org.apache.myfaces.lifecycle.InvokeApplicationExecutor.execute(InvokeApplicationExecutor.java:32)
                                    at org.apache.myfaces.lifecycle.LifecycleImpl.executePhase(LifecycleImpl.java:103)
                                    at org.apache.myfaces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:76)
                                    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:148)
                                    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
                                    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                                    at org.apache.myfaces.webapp.filter.ExtensionsFilter.doFilter(ExtensionsFilter.java:147)
                                    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
                                    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                                    at org.ajax4jsf.webapp.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:177)
                                    at org.ajax4jsf.webapp.BaseFilter.handleRequest(BaseFilter.java:267)
                                    at org.ajax4jsf.webapp.BaseFilter.processUploadsAndHandleRequest(BaseFilter.java:380)
                                    at org.ajax4jsf.webapp.BaseFilter.doFilter(BaseFilter.java:507)
                                    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
                                    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                                    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
                                    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
                                    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:558)
                                    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
                                    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
                                    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
                                    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
                                    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:852)
                                    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
                                    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
                                    at java.lang.Thread.run(Thread.java:619)
                                Caused by: javax.faces.el.MethodNotFoundException: org.apache.jasper.el.JspMethodNotFoundException: /body/network/body_network.jsp(257,4) '#{NetworkBean.navigateFromModalPanel}' Method not found: web.NetworkBean@23af64.navigateFromModalPanel()
                                    at javax.faces.component._MethodExpressionToMethodBinding.invoke(_MethodExpressionToMethodBinding.java:77)
                                    at org.apache.myfaces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:54)
                                    ... 31 more
                                Caused by: org.apache.jasper.el.JspMethodNotFoundException: /body/network/body_network.jsp(257,4) '#{NetworkBean.navigateFromModalPanel}' Method not found: web.NetworkBean@23af64.navigateFromModalPanel()
                                    at org.apache.jasper.el.JspMethodExpression.invoke(JspMethodExpression.java:71)
                                    at javax.faces.component._MethodExpressionToMethodBinding.invoke(_MethodExpressionToMethodBinding.java:75)
                                    ... 32 more
                                2010-04-19 11:58:48,443 WARN [org.apache.myfaces.renderkit.html.util.DefaultAddResource] - MyFaces special javascript could not be retrieved from request-map.
                                

                                 

                                Any idea? Thanks again

                                • 13. Re: Warn by leaving a form view with unsaved changes
                                  Harut Sargsyan Apprentice
                                  Caused by: javax.faces.el.MethodNotFoundException: org.apache.jasper.el.JspMethodNotFoundException: /body/network/body_network.jsp(257,4)

                                  This means you haven't added action method which you are trying to call by pressing h:commandbutton 

                                  action="#{myBean.navigateFromModalPanel}"

                                  Add that action method into your bean

                                   

                                  public String navigateFromModalPanel() {

                                       return navigateFromModalPanel;

                                  }

                                  1 of 1 people found this helpful
                                  1 2 Previous Next