6 Replies Latest reply on Mar 24, 2008 6:50 PM by Mikael Andersson

    Datagrid, @Factory, actions and reRendering

    Oleg Puzanov Newbie

      Hello,


      I have the following problem with my attempt to rerender rich:datagrid upon selecting an item from the tree control on the same page.


      This tree control contains labels with category names.
      So upon choosing a certain category I would like to redraw the datagrid with elements only from the chosen category.


      Problems:


      1) @Factory method, which feeds the datagrid elements, is being called by Seam before action method defined in a4j:support.


      2) @Factory method is being called even when I remove reRender attribute in a4j:support.


      I really need to call action method before @Factory is being called by Seam. Or to control the invocation of @Factory somehow.


      This code is very similar to mine:


      ++ category.xhtml
      ......
      
      <h:outputLabel value="#{category.name}" >
           <a4j:support event="onclick" action="#{categoryaction.selectCategory(category.name)}" reRender="categoryRegion" ajaxSingle="true"/>  
      </h:outputLabel>
      ......
      
      <a4j:region id="categoryRegion">
           <rich:dataGrid id="categoryGrid" width="670" columns="1" value="#{categories}" var="category" border="0">
                  ......
                  ......
           </rich:dataGrid>
      </a4j:region>
      ......
      
      ++ CategoryActionBean.java
      .......
      
      @Factory("categories")
      public void listAllCategories() {                                       
          categories = getAllCategories();            
      }
      .......
      



      So the main target is to call categoryaction.selectCategory(category.name) before @Factory method is being fired for categories variable.


      I really appreciate your help.


      Thanks,
      Oleg

        • 1. Re: Datagrid, @Factory, actions and reRendering
          Oleg Puzanov Newbie

          After reading some info about @Factory behavior, I understood that it will be always called before action methods. And there is no other way out.


          So I've tried to remove @Factory and to call the bean method directly in datagrid:


          ............
          
          public List<Category> listAllCategories() {                                   
              categories = getAllCategories();          
          }
          ............
          
          <rich:dataGrid id="categoryGrid" width="670" columns="1" value="#{categoryaction.listAllCategories()}" var="category" border="0">
                      ......
                      ......
          </rich:dataGrid>
          



          But everything remained the same - listAllCategories() is being called before the action method in a4j:support.


          Can somebody explain such behavior?


          Thanks,
          Oleg


           

          • 2. Re: Datagrid, @Factory, actions and reRendering
            Oleg Puzanov Newbie

            At first - I've noticed that my previously posted code chunks contained several errors. I've written them only for this forum message and for showing the overall idea, please don't pay attention to the details.


            Now about the current status of this problem:


            1) As I said before - I need to pass the filtering parameters for datagrid. One of these parameters is category, which is being selected via the tree control.


            2) Tree control nodes contain labels with the category names.
               Upon clicking on each label I want the datagrid to be re-rendered using a4j:support in h:outputLabel.
             
            3) Previously this datagrid has been filled via @Factory, now I've switched to the regular bean method (see above).


            4) Filtering parameters are being passed via calling the setter-methods of SFSB.


            5) Each corresponding filter-property of SFSB is being marked  with @In and @Out. So they will become available in the datagrid-feeding method, which is being called in a separate conversation...


            6) Everything works fine, but EXTREMELY slowly... Unacceptably slowly. And in general the overall approach looks very ugly.


            So I've just started thinking one more time that there is a well-known approach for handling this scenario - passing the filtering parameters for datagrid.


            It is pretty much clear that if I could use input controls (e.g. h:inputText), then this problem would become irrelevant.
            But I need to use h:outputLabel and to pass its current value as the filtering parameter.


            Please help me with this question, thanks in advance.     

            • 3. Re: Datagrid, @Factory, actions and reRendering
              Marcell Newbie

              hi Oleg,


              Unfortunately I am in a machine that does not allow me to open Eclipse, then I can't help you until tomorrow. However I will point out some things I notice:



              1. The factory method is called every time it is needed. However, if it has been evaluated (i.e. there is a context value defined for the factory and this value isn't null) then it won't be re-evaluated. The use of @Factory can speed up execution because the method binding can be called lot of times inside the JSF life cycle and the @Factory avoids the re-execution.

              2. Try to use the ajaxSingle="true" property at a4j:support or your whole form will be submitted (AFAIK). The use o immediate="true" is also recommended.

              3. Don't know why without reRender your dataGrid is been re-draw. I need to see the whole page to suggest something.

              4. I'm not sure of this, but if you unset the value of @Factory at the end of the action method you will get a re-evaluation at the dataGrid reRender. You can do that by calling Contexts.removeFromAllContext("categories").



              Try the above, give me a reply, and tomorrow I'll look my code to help you more.


              • 4. Re: Datagrid, @Factory, actions and reRendering
                Oleg Puzanov Newbie

                Hi Marcell,


                Thanks for your detailed answer.


                Here is my set of comments for your three points:


                1. You're right - @Factory won't be re-evaluated if it's not null.
                But what will happen with @Factory for the following two cases:


                  a) Datagrid contents did not change and we've called reRender via a4j:support
                  b) Datagrid contents did changed and we've called reRender via a4j:support


                Will there be any differences?
                By saying changed and not changed I meant the list of objects returned by @Factory method.


                Another point - currently I've made a conclusion that @Factory is not very appropriate for the frequently updated tables and lists. For example: if the user clicks multiple filtering and sorting buttons for the datagrid, then @Factory is not a good option for feeding this datagrid.


                What do you think about this?


                2. Regarding the use of ajaxSingle and immediate - the first one is clear. And the second one? Are there any default delays or default queuing of AJAX requests?


                3. This has been fixed already, minor issue.


                4. Nice option. So I can initiate the re-evaluation of @Factory by clearing its context variable, right? But in any case @Factory will be called before the action method in the most beginning. So this means that we will have two @Factory re-evaluations.


                 


                • 5. Re: Datagrid, @Factory, actions and reRendering
                  Marcell Newbie

                  1. You're right - @Factory won't be re-evaluated if it's not null.
                  But what will happen with @Factory for the following two cases:
                  a) Datagrid contents did not change and we've called reRender via a4j:support
                  b) Datagrid contents did changed and we've called reRender via a4j:support
                  Will there be any differences?
                  By saying changed and not changed I meant the list of objects returned by @Factory method.


                  As I said, the @Factory doesn't know if its contents changed or not. It will only re-evaluate (i.e. call the method contents to produce the changes) its value is null or undefined. Then, (a) and (b) for a already evaluated @Factory is the same, and it will only return the evaluated value.



                  Another point - currently I've made a conclusion that @Factory is not very appropriate for the frequently updated tables and lists. For example: if the user clicks multiple filtering and sorting buttons for the datagrid, then @Factory is not a good option for feeding this datagrid.
                  What do you think about this?


                  I am using the @Factory a lot, but using the hack the I described at (4). But I have to say that this exactly problem made me switch, before, to your approach (use the bean method), however I thought it was a worse hack and I found the solution at (4).



                  2. Regarding the use of ajaxSingle and immediate - the first one is clear. And the second one? Are there any default delays or default queuing of AJAX requests?


                  AFAIK the immediate is almost like the a4j:region, except that immediate is to only 1 component.



                  4. Nice option. So I can initiate the re-evaluation of @Factory by clearing its context variable, right? But in any case @Factory will be called before the action method in the most beginning. So this means that we will have two @Factory re-evaluations.


                  I am not sure about action methods, but to value change listener it works sweet.
                  It will not be 2 re-evaluations! The 1st one will return the already evaluated value and the 2nd, I think at the JSF render phase, it will re-evaluate because now the @Factory value is undefined.

                  • 6. Re: Datagrid, @Factory, actions and reRendering
                    Mikael Andersson Master

                    Hi


                    Might this work for you?


                    - Use the @Factory for initializing the list, during bean creation. (which is what the @Factory annotation is meant for)


                    - Put logic in your selectCategory(..) action method to update the categories list; filtering the content.


                    What is causing the slowdown? Is the getAllCategories() hitting the database? If it is it is a really bad idea to use it in a getter without caching the value( for example just checking for null).


                    Also, is the backing bean in session scope?


                    Cheers,
                    Micke