8 Replies Latest reply on Mar 31, 2011 5:03 AM by Raimund Hölle

    How to display StatusMessages in AJAX re-render callbacks

    Raimund Hölle Newbie

      Hello,


      we're wondering what is the correct approach to create status messages within the AJAX callbacks on re-rendering.


      At this point of the lifecycle, StatusMessages are already transmitted to the JSF context so newly createed messages are transmitted not until the next lifecycle.


      To be more create, assume the following use case. We have any value which depends on other input fields. On changing that input fields, our value should be checked to ensure data consistency:




      <rich:messages .../>
      
      <rich:calendar value="#{myAction.date}" ...>
        <a:support event="onchanged" 
                   reRender="actualValue" 
                   bypassUpdates="false" 
                   ajaxSingle="true" />
      </rich:calendar>
      
      <h:outputText id="actualValue" value="#{myAction.actualValue}" />
      
      @Name("myAction")
      class MyAction {
      
        @In(create = true)
        private StatusMessages statusMessages;
      
        private Date   date;
        private String actualValue;
        
        public Date getDate() {
          return Date;
        }
        public void setDate(Date date) {
          this.date = date;
        }
        public String getActualValue() {
          if ( ! isActualValueAllowedForDate() ) {
            statusMessages.add("The actual value isn't allowed for this input.");
          }
          return actualValue;
        }
      }             
      


      Even though rich:messages is automatically re-rendered, we don't see the message because it isn't transferred to the JSF-context yet.


      To avoid that problem, we found two approaches:



      1. Instead of using StatusMessages we directly add the messages to the JSF context.

      2. We force StatusMessages to synchronize with JSF context using the following code:





      statusMessages.add("The actual value isn't allowed for this input.");
      synchronizeStatusMessages();
      
      void synchronizeStatusMessages() {
        if ( Contexts.isConversationContextActive() ) {
          Object o = Component.getInstance( FacesMessages.COMPONENT_NAME );
          if (o instanceof FacesMessages) {
            FacesMessages.afterPhase();
            ((FacesMessages) o).beforeRenderResponse();
          }
        }
      }
      
      



      Anyone who knows a better solution?


      Regards, Raimund


      (Note: we're using SEAM 2.2.1.Final).



        • 1. Re: How to display StatusMessages in AJAX re-render callbacks
          Serkan Eskici Novice
          Try to remove ajaxsingle=true and add ajaxrendered=true on rich:messages (or around it with a4j:outputpanel).
          • 2. Re: How to display StatusMessages in AJAX re-render callbacks
            Raimund Hölle Newbie

            That will not help. The problem isn't the view, it is solely caused by the implementation of FacesMessages which buffers the message up to the next pre-rendering phase.


            BTW,

            ajaxrendered=true

            is the default of rich:messages.

            • 3. Re: How to display StatusMessages in AJAX re-render callbacks
              Serkan Eskici Novice

              I have the following code and it just works.


              /taglib/messages.xhtml:


              <a4j:outputPanel id="errors" layout="block" ajaxRendered="true">
                  <s:div styleClass="errors" rendered="#{validation.failed or (global ? not facesMessages.getCurrentMessages().isEmpty(): not empty errors)}">
                      <strong>#{not empty title ? title : 'Please correct the following errors: '}</strong>
                      <rich:messages showSummary="true" showDetail="false" level="#{global ? 'ALL' : 'WARN,ERROR'}"/>
                  </s:div>
              </a4j:outputPanel>
              




              /form.xhtml:


              <h:inputText value="" required="true" label="date">
                 <a4j:support event="onblur" reRender="myDiv" /> 
                 <s:validate />
              </h:inputText>
              



              myDiv gets re-rendered, but also rich:messages when validation error occurs.


              • 4. Re: How to display StatusMessages in AJAX re-render callbacks
                Raimund Hölle Newbie

                That's a completely different thing. The validation messages are created in the validation phase, t. m., before FacesMessages emits the messages to the JSF context in the pre-render phase.


                We're speaking about messages which are created in the render phase, t. m. after FacesMessages has emitted its buffer.


                Again, that problem has nothing to do with JSF or Richfaces, it's only a SEAM feature.

                • 5. Re: How to display StatusMessages in AJAX re-render callbacks
                  Tim Evers Master

                  To be fair this isn't anything to do with Seam either. This just comes down to understanding JSF phases.


                  Once you are in the render response phase you really shouldn't be making changes to the view (I know you can, and in some circumstances it is unavoidable). The outcome can be unpredictable. It doesn't matter that this is an AJAX response.


                  Secondly, getters are not a very good place for logic. There is no way you can be sure that the method will only be called once. In which case you may end up with the same message multiple times in your list of messages. In fact it is quite rare that the getter will only be called once.


                  The proper way to handle this is as far as I know is by using the action attribute of the a4j:support tag.


                  What I've got below is rough and completely untested so....it is just to get the idea across.


                  <rich:messages .../>
                  
                  <rich:calendar value="#{myAction.date}" ...>
                    <a:support event="onchanged" 
                               reRender="actualValue" 
                               bypassUpdates="false"
                               action="#{myAction.validateData()}" 
                               ajaxSingle="true" />
                  </rich:calendar>
                  
                  <h:outputText id="actualValue" value="#{myAction.actualValue}" />
                  
                  @Name("myAction")
                  class MyAction {
                  
                    @In(create = true)
                    private StatusMessages statusMessages;
                  
                    private Date   date;
                    private String actualValue;
                    
                    public Date getDate() {
                      return Date;
                    }
                    public void setDate(Date date) {
                      this.date = date;
                    }
                    public String getActualValue() {
                      return actualValue;
                    }
                    public void validateData() {
                      if ( ! isActualValueAllowedForDate() ) {
                        statusMessages.add("The actual value isn't allowed for this input.");
                      }
                    }
                  } 
                  



                  • 6. Re: How to display StatusMessages in AJAX re-render callbacks
                    Raimund Hölle Newbie

                    Hello,


                    thanks for your suggestion. I agree, in most cases that is a better approach than mine.


                    But it seems to have also limits.


                    First, the dependency between view and backing bean increases due to the need of calling additional actions instead of only requesting the data. Not a big thing, but should be considered by designing complex masks.


                    Second, in complex dialogs with a lot of dependencies between controls (also cyclic!) it is not easy to resolve such dependencies in the backing bean, especially if the same backing bean should be used by more than one view.


                    For such complex dialogs we're using a proxy- or ondemand pattern; the initial example was only a very simplified illustration for that. (BTW, that pattern avoids problems with calling the same getter multiple times).


                    However, in the future i will try to use action methods to send user messages.


                    Besides the limits of StatusMessages, what other possible problems may occur on manipulating the data models in the render phase?


                    Thanks a lot, Raimund

                    • 7. Re: How to display StatusMessages in AJAX re-render callbacks
                      Tim Evers Master

                      Raimund Hölle wrote on Mar 30, 2011 09:48:


                      Hello,

                      thanks for your suggestion. I agree, in most cases that is a better approach than mine.

                      But it seems to have also limits.

                      First, the dependency between view and backing bean increases due to the need of calling additional actions instead of only requesting the data. Not a big thing, but should be considered by designing complex masks.


                      Requesting the data requires the backing bean so there is no increased dependency at all. If you need the bean n times then n+1 is no different.



                      Second, in complex dialogs with a lot of dependencies between controls (also cyclic!) it is not easy to resolve such dependencies in the backing bean, especially if the same backing bean should be used by more than one view.


                      I don't understand the logic here. If you can resolve the dependencies in a view and all information in a view comes from a backing bean....how is it not possible to work it out in the backing bean? You've already worked it out.



                      For such complex dialogs we're using a proxy- or ondemand pattern; the initial example was only a very simplified illustration for that. (BTW, that pattern avoids problems with calling the same getter multiple times).


                      I am not familiar with this pattern you are talking about. (Or maybe I am and have not seen it called by this name). From the example you gave above unless all your beans are wrapped in a proxy that caches the result of a method call then calling that method multiple times could result in multiple messages. If your using a proxy with cached values then sure, but how long does your cache last? I can see it actually returning wrong results if the cache lasts too long.



                      However, in the future i will try to use action methods to send user messages.


                      I think this point is more important then people give credit for. Putting logic inside getters when dealing with JSF is a complex problem. Getters get called during almost every phase of the JSF lifecycle. putting logic in them requires you to cater for this fact and most people don't because it appears to just work. But, as your application becomes more complex and logic becomes more spread it can become somewhat of a nightmare.


                      If a getter is just a getter then the next programmer that comes along can feel very comfortable that the only way a value can change during the JSF lifecycle is if an action gets called. This will make the complexity of resolving issues much smaller.



                      Besides the limits of StatusMessages, what other possible problems may occur on manipulating the data models in the render phase?

                      Thanks a lot, Raimund


                      Well, take another look at the JSF lifecycle. How many paths are there to the render response phase? It certainly is NOT 1. Failure/Break conditions in any of the previous phases will result in an immediate jump to render response phase. Is your code prepared for this? The apply request values may have been applied and it failed elsewhere does your logic handle that?


                      What about parallel ajax requests? Are you going to run into synch problems? Obviously not an issue if your running a queue which is pretty much mandatory for Seam apps anyway.


                      I think when you consider all the possibilities (which I really don't even know myself) I would expect the most logical conclusion to be that manipulating the model during the render response phase may not be the wisest move. I would then be asking myself, why is it that my application invoke phase can't do this work? To me it would indicate a flaw in my design. There's probably a better way.

                      • 8. Re: How to display StatusMessages in AJAX re-render callbacks
                        Raimund Hölle Newbie

                        Thanks a lot for the helpful explanations. Now I can comprehend why it is difficult to perform substantial actions in the render phase.



                        Tim Evers wrote on Mar 30, 2011 18:48:



                        Raimund Hölle wrote on Mar 30, 2011 09:48:


                        the dependency between view and backing bean increases due to the need of calling additional actions instead of only requesting the data. Not a big thing, but should be considered by designing complex masks.


                        Requesting the data requires the backing bean so there is no increased dependency at all. If you need the bean n times then n+1 is no different.


                        What I meant was the dependency between view code and backing bean code, not the object dependency: instead of just calling the getters i have also to call additional actions at the correct locations.






                        Second, in complex dialogs with a lot of dependencies between controls (also cyclic!) it is not easy to resolve such dependencies in the backing bean, especially if the same backing bean should be used by more than one view.



                        I don't understand the logic here. If you can resolve the dependencies in a view and all information in a view comes from a backing bean....how is it not possible to work it out in the backing bean? You've already worked it out.



                        In a complex dialog with different selections lists, assignment controls like rich:listShuttle which contents depends on the selection of each other it is sometimes difficult to find the right order of retrieving / creating the data - that was the dependencies i meant.





                        For such complex dialogs we're using a proxy- or ondemand pattern; the initial example was only a very simplified illustration for that. (BTW, that pattern avoids problems with calling the same getter multiple times).


                        I am not familiar with this pattern you are talking about. (Or maybe I am and have not seen it called by this name).



                        Sure you are. See the following simple code example:




                        public MyData getMyData() {
                          if (myData == null) {
                            myData = new MyData();
                          }
                          return myData;
                        }




                        The data object is created on demand, t. m. when it is requested the first time. Using a proxy works the same but may be used for more complex structures. Such a pattern may help to resolve dependencies on the order of object creation.




                        From the example you gave above unless all your beans are wrapped in a proxy that caches the result of a method call then calling that method multiple times could result in multiple messages. If your using a proxy with cached values then sure, but how long does your cache last? I can see it actually returning wrong results if the cache lasts too long.



                        My example seems to be very inappropriate for that discussion, it was only written to explain my problem with StatusMessages.


                        However, as I shown in the example above, not different beans are cached, only the data provided by the backing bean in question. Because the data lives in the backing bean it isn't a challenge to define its lifetime.


                        It is a common used programming approach. But, as I can see now, it may not be recommended together with JSF backing beans.


                        Once again, thanks a lot for your patience and good explanations.