10 Replies Latest reply on Nov 15, 2007 9:43 AM by pmuir

    pages.xml action method fired on ajax requests - must be wro

      Hi,

      I have just spent the whole day re-structuring my seam application to make use of the action method on a page element in pages.xml only to find to my dismay that the action method fires not only on redirects to the page (as it should) but also on ajax callbacks.

      This should not be the case as the action method on the page should fire on the first time load of the page only, thus a perfect place to put initial model setup logic (or to ensure the model is setup so that pages.xml can redirect elsewhere if not, say by using an if-outcome etc), whilst all the ajax calls are technically working with the same page via postbacks (and hence don't need the setup logic).

      What this means is that the setup logic that I put into action method gets called ALL the time effectively eliminating the work of my ajax postback to change the model in order to change the display. i.e. during the rendering phase the setup logic gets called again, changing the model back to the initial page load state (and un-doing the work the ajax call did in the first place).

      My specific example is as follows

      pages.xml fragment

      <page view-id="/pm_ncrv.xhtml" action="#{maintainComponent.setupRelationshipsView}" login-required="true">
      ...
      </page>
      


      Now there is an ajax callback in the page as follows

      <a4j:outputPanel id="childAttributes">
       <s:div rendered="#{not attributeProps.show}">
       <a4j:commandLink
       action="#{maintainComponent.addNewChildComponent}"
       immediate="true" value="Add new child component"
       reRender="childAttributes"
       oncomplete="initFileUploads()"
       styleClass="button" />
       </s:div>
       <s:div rendered="#{attributeProps.show}">
       ...
       </s:div>
      </a4j:outputPanel>
      


      Now the setup method setupRelationshipsView() called in the action parameter of pages.xml does the following so that the ajax panel childAttributes is not rendered when you first redirect to the page:

      public void setupRelationshipsView() {
       log.info("setupRelationshipsView()");
       component = null;
       attributeProps = new AttributeProperties();
       attributeProps.setLegendText("Child Component Attributes");
       attributeProps.setReadOnly(false);
       attributeProps.setShow(false);
      }
      


      The ajax call addNewChildComponent() wants to show the child attributes panel inline so that a new child component can be added so it does this:

      public void addNewChildComponent() {
       log.info("addNewChildComponent():mainComponent=" + mainComponent);
       if (mainComponent == null) {
       facesMessages.add("Main component not in correct state. " +
       "Child component could not be added. Please check log for errors.");
       log.error("addNewChildComponent():mainComponent not " +
       "in correct state:mainComponent=" + mainComponent);
       }
       else {
       if (mainComponent.getChildComponents() == null) {
       mainComponent.setChildComponents(new ArrayList<ISISComponent>());
       }
       component = new ISISComponent();
       component.setParentComponent(mainComponent);
       imageHolder = component;
       attributeProps = new AttributeProperties();
       attributeProps.setLegendText("Add new child component attributes");
       attributeProps.setReadOnly(false);
       attributeProps.setShow(true);
       }
      }
      


      In other words it sets the show flag on attributeProps so that the inline panel is displayed. BUT... from the logs I see that the pages.xml action method gets called after the ajax call which resets the model variable attributeProps so that the inline panel is never displayed.

      21:38:43,044 INFO [MaintainComponentAction] addNewChildComponent():mainComponent=model.ISISComponent@780767
      21:38:43,053 INFO [MaintainComponentAction] setupRelationshipsView()
      


      How does one stop the pages.xml from firing on ajax callbacks? I would consider this a major architectual flaw if this could not be done!! HELP!

      Thanks

      Troy

      Seam 2.0.0 BETA
      RichFaces 3.1.0
      Jboss 4.2.0.GA


        • 1. Re: pages.xml action method fired on ajax requests - must be

          I have an ugly workaround for this and it goes as follows: On the ajax call I also attach an actionListener which sets a flag on my model that the call is indeed an ajax call.

          When the page action method runs before rendering, I check this flag and exit straight away if this is set so that the setup logic is not executed on a ajax callback. I also reset the flag as well. So

           <a4j:commandLink
           action="#{maintainComponent.addNewChildComponent}"
           actionListener="#{maintainComponent.isAjax}"
           immediate="true" value="Add new child component"
           reRender="childAttributes" oncomplete="initFileUploads()"
           styleClass="button" />
          


          And

          boolean isAjax = false;
           public void isAjax(ActionEvent event) {
           log.info("isAjax()");
           isAjax = true;
           }
          
           private boolean checkAjax() {
           boolean result = isAjax;
           isAjax = false;
           return result;
           }
          
          ...
          
          public void setupRelationshipsView() {
           log.info("setupRelationshipsView():isAjax=" + isAjax);
           if (checkAjax()) return;
          ...
          
          
          


          Pretty nasty, it would be good if there was some control over the calling of the action method on pages.xml....

          Troy


          • 2. Re: pages.xml action method fired on ajax requests - must be
            pmuir

            This behaviour is to be expected due to the way a:commandButton works.

            You could perhaps file a feature request in JIRA and we'll see what we can come up with/whether it is a popular feature.

            • 3. Re: pages.xml action method fired on ajax requests - must be
              dkane

               

              "pete.muir@jboss.org" wrote:
              This behaviour is to be expected due to the way a:commandButton works.

              You could perhaps file a feature request in JIRA and we'll see what we can come up with/whether it is a popular feature.


              Was the JIRA issue raised ? I have the same (?) problem with page action and ajax request.

              The first version was without page action. My page is master-detail one. Upper panel contains rich:dataTable with ajax support for onRowClick event. Lower panel contains the detail dataset updated and rerednered every time when row in upper (master) dataset is clicked. Standard pattern.

              It was fine until I click "refresh" in browser. Browser asked me if form data should be resubmitted, and after that detail dataset got syncronized with previously, not currently selected row.

              I decided to use page action to refresh both datasets upon "Reload" click. And then noticed : each row click event with a:support invokes page action.

              Any other way to intercept page reload, or tune page action ? Components are session-scoped..



              • 4. Re: pages.xml action method fired on ajax requests - must be

                I didn't raise a JIRA, how does one do that? I also thought about it a bit more and changed my architecture to suit, however I still think there is a need to detect first time page load and have an option to run code there. It's kinda like .net has this feature where you can detect a 'post back' and do logic accordingly.

                I'm not saying that we should do that, but rather since its so easy to do ajax in a page it would be nice to have an option for 'setting' up the page and have it run that once.

                • 5. Re: pages.xml action method fired on ajax requests - must be
                  pmuir

                  I'm not really in love with the idea.

                  • 6. Re: pages.xml action method fired on ajax requests - must be

                    I'm not too keen either, however the only scenario that I'm struggling to solve elegantly is the scenario where you have a page that can be navigated to in a number of ways and before you display that page you need the model to be setup correctly, i.e. you need to query something or change state in say a conversation before the page is rendered. And you can't rely on starting a conversation as you are already in one.

                    Now <page view-id="..." action="#{setupmethod}"> looks like a good candidate to centralise this behaviour, i.e. it doesn't matter how you get to this page you know the model is going to be 'prepared' for it in a pull manner.

                    However you only want this to happen in two cases, the first time you land on the page and when the user refreshes the page (which is arguably re-landing on the same page). You don't want this code to run when you are interacting with the page. Now the traditional GET and POST solve this problem as you know where you are, but with restful urls this becomes difficult.

                    The only other way (I can think of) is to track all navigation requests to the page and call some setup code in a push manner (not as elegant and maintenance proof) and then redirect to the page and not use <page view-id="" action="#{setupMethod}"> at all.

                    It was my understanding from reading the docu that the action method on the element was designed for implementing some sort of MVP pattern?



                    • 7. Re: pages.xml action method fired on ajax requests - must be
                      pmuir

                      Well add a feature request to JIRA and we'll see if its popular or not ;)

                      • 8. Re: pages.xml action method fired on ajax requests - must be

                        Or, perhaps the "action" element can take an attribute that says "onlyAtPageLoad=true"?

                        • 9. Re: pages.xml action method fired on ajax requests - must be
                          stephen.friedrich

                           

                          "pete.muir@jboss.org" wrote:
                          I'm not really in love with the idea.


                          Hm, why not?

                          Use case:
                          * a GET request loads a page,
                          * page action starts a conversation
                          * the initial page load contains an edit form
                          * conversation continues while another page containing more details is loaded
                          * navigation returns to the original page (have to set join="true" in the page's begin-conversation tag to make this work).

                          Now I'd like to be able to fetch persistent data only on initial page load
                          (to avoid a DB roundtrip when the page is loaded again.)

                          How do I do that without support for page actions that fire only on first access in a conversation?

                          • 10. Re: pages.xml action method fired on ajax requests - must be
                            pmuir

                            I agree the use case is valid, but I don't like the proposed solution. But then I don't know what is better... I will think on it.