10 Replies Latest reply on Aug 6, 2008 7:34 PM by Ken Clark

    Using Seam EL to pass parameters to Action methods - doesn't always work

    Ken Clark Newbie

      I am trying to understand something fundamental about how Seam works - I think.


      I know Seam extends EL to support passing parameters to action methods.  This is very cool and (can be) extremely useful.  However, it has its limits, and I am not sure I understand what they are, except I am learning bit by bit using trial and error.


      I know that if I am iterating in an h:dataTable, sometimes I can use the iterated variable:





      • If the iterator is over an outjected @DataModel, then usually the iterated variable will work.






      • If the iterator is over a collection that is a child of an outjected object, then usually the iterated var will NOT work




      I also usually have good luck if the passed variable is a direct reference to any scoped object.


      So my questions are:


      1) Why can't I pass an iterated var if the iteration is over a child collection?


      2) I am guessing this is all related to scope in some way, but are there any hard and fast rules about what I can and cannot pass this way?  If so, I greatly appreciate a link to any documentation.


      Here is some facelet code that illustrates a non-working use of this capability:



      <h:dataTable value="#{templateNode.childrenNodes}" var="childNode">
          <h:column>
              <f:facet name="header">
                  <h:outputText value="Node Id"/>
              </f:facet>
              <s:link id="childLink" 
               action="#{linkAction.navigate(childNode)}"
               value="#{childNode.title}"/>
          </h:column>
      </h:dataTable>





      In this snippet of code, childNode.title works fine when rendering, but when the navigate(Node n) method is called, it always gets a null value for n.



      Thanks,
      ken


        • 1. Re: Using Seam EL to pass parameters to Action methods - doesn't always work
          Daniel Hinojosa Master

          Too many possiblities of error



          • What is the scope of templateNode?

          • What is templateNode.childrenNodes returning is your second case a plain collection, array list, a set?



          • 2. Re: Using Seam EL to pass parameters to Action methods - doesn't always work
            Ken Clark Newbie

            templateNode is outjected by my Action class as the default scope.  There is a long-running conversation.  I can pass in the templateNode reference itself and it works as I expect.


            childrenNodes is a


            List<Node>
            


            The list is implemented as a subclass of ArrayList.


            Thanks,
            ken

            • 3. Re: Using Seam EL to pass parameters to Action methods - doesn't always work
              Ken Clark Newbie

              I can also change the action as such and it does send in the value I expect:



              <s:link id="childLink" 
                      action="#{linkAction.navigate(templateNode.childrenNodes.get(0))}" value="#{childNode.title}"/>
              



              • 5. Re: Using Seam EL to pass parameters to Action methods - doesn't always work
                System Administrator Expert

                The list is implemented as a subclass of ArrayList?

                what is that exactly?


                typically it's like this:


                List<Node> list = new ArrayList<Node>();


                have you tried to replace <s:link> with something like <h:commandButton> to see what happens?  although most likely that's not the problem.  it seems to me that your design/strategy should work.


                have you looked at @DataModelSelection?  it will inject the current row's instance of the clickable dataTable (DataModel)...

                • 6. Re: Using Seam EL to pass parameters to Action methods - doesn't always work
                  Daniel Hinojosa Master

                  I redid Ken's scenario, you can try it for yourself...


                  Here is an action bean:


                  @Name("linkAction")
                  @Scope(ScopeType.CONVERSATION)
                  public class ActionBean {
                      public void navigate(Node node) {
                          System.out.println(node);
                      }
                  
                      @Begin
                      @Factory(value = "templateNode", scope= ScopeType.CONVERSATION)
                      public Node startupTemplateNode() {
                          System.out.println("starting template Node");
                          Node parent = new Node("bob");
                          parent.addChild(new Node("ann"));
                          parent.addChild(new Node("carl"));
                          parent.addChild(new Node("jesus"));
                          return parent;
                      }
                  }
                  



                  Here is a Node Bean


                  public class Node {
                      private String title;
                      private List<Node> children;
                  
                      public Node(String title) {
                          this.title = title;
                          this.children = new ArrayList<Node>();
                      }
                  
                      public String getTitle() {
                          return title;
                      }
                  
                      public void setTitle(String title) {
                          this.title = title;
                      }
                  
                      public void addChild(Node node) {
                          this.children.add(node);
                      }
                  
                      public List<Node> getChildren() {
                          return children;
                      }
                  }
                  



                  Here is a web page:


                  <?xml version="1.0" encoding="UTF-8"?>
                  <!DOCTYPE html
                          PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
                  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
                        xmlns:s="http://jboss.com/products/seam/taglib"
                        xmlns:ui="http://java.sun.com/jsf/facelets"
                        xmlns:f="http://java.sun.com/jsf/core"
                        xmlns:h="http://java.sun.com/jsf/html"
                        xmlns:t="http://myfaces.apache.org/tomahawk"
                        xmlns:rich="http://richfaces.org/rich">
                  <head>
                      <title>Righteous@!</title>
                  </head>
                  <body>
                  <h:dataTable value="#{templateNode.children}" var="childNode">
                      <h:column>
                          <f:facet name="header">
                              <h:outputText value="Node Id"/>
                          </f:facet>
                          <s:link id="childLink"
                                  action="#{linkAction.navigate(childNode)}"
                                  value="#{childNode.title}" propogation="join"/>
                      </h:column>
                  </h:dataTable>
                  </body>
                  </html>
                  

                  • 7. Re: Using Seam EL to pass parameters to Action methods - doesn't always work
                    Daniel Hinojosa Master

                    Ken and Arbi,


                    I think that Ken's first analysis is correct.  You need a backing @DataModel so that when the action is called it can determine what row is selected.  By Ken having a link with an action, it is essentially a glorified h:commandLink, and we know that if you use a commandLink, you definitely need a @DataModel to make that selection. 


                    In the case, Ken, that you need to use <s:link> purely to call out a restful service, and not select that row,  then of course you don't need a @DataModel, but anytime a row needs to be selected somehow you do require a @DataModel.  I verified this in the Seam in Action, although the book didn't specifically state this, the example provided did.


                    P.S. Ken, I added some @Begin on my bean and propogation=join on s:link in order to ensure that a long-running conversation is running. 


                    • 8. Re: Using Seam EL to pass parameters to Action methods - doesn't always work
                      Ken Clark Newbie

                      Arbi Sookazian wrote on Aug 05, 2008 21:31:


                      The list is implemented as a subclass of ArrayList?

                      what is that exactly?

                      typically it's like this:

                      List<Node> list = new ArrayList<Node>();


                      It's like this:


                      List<Node> l = new ChildrenArrayList<Node>();
                      



                      I subclassed ArrayList for my own needs (it keeps parents in synch no matter how daft a developer might be)





                      have you tried to replace <s:link> with something like <h:commandButton> to see what happens?  although most likely that's not the problem.  it seems to me that your design/strategy should work.

                      have you looked at @DataModelSelection?  it will inject the current row's instance of the clickable dataTable (DataModel)...


                      I think I have seen the same thing in a command button in the past -- it seems related to the EL processing itself (and how it finds objects to pass as parameters) as opposed to the particular component.


                      As for the DataModelSelection -- yes, I use that elsewhere, and may be forced to use it here also.  Actually, one of the reasons I was using parameters in EL was specifically to avoid having to use DataModel and DataModelSelection.


                      Thanks,
                      ken

                      • 9. Re: Using Seam EL to pass parameters to Action methods - doesn't always work
                        Ken Clark Newbie

                        Daniel Hinojosa wrote on Aug 05, 2008 22:01:


                        Ken and Arbi,

                        I think that Ken's first analysis is correct.  You need a backing @DataModel so that when the action is called it can determine what row is selected.  By Ken having a link with an action, it is essentially a glorified h:commandLink, and we know that if you use a commandLink, you definitely need a @DataModel to make that selection. 

                        In the case, Ken, that you need to use <s:link> purely to call out a restful service, and not select that row,  then of course you don't need a @DataModel, but anytime a row needs to be selected somehow you do require a @DataModel.  I verified this in the Seam in Action, although the book didn't specifically state this, the example provided did.

                        P.S. Ken, I added some @Begin on my bean and propogation=join on s:link in order to ensure that a long-running conversation is running. 




                        Thanks for the analysis.  I was going to ask you after your earlier post -- but based on what you just wrote here, I gather that your experiment did fail in the same way that mine did?


                        By the way, your code is mostly the same as mine.  In my case I was using a SFSB that is not Conversational (the conversations are managed with @Begin and @End).  Everything else is basically the same.


                        As you may notice from my previous email, I would like to be able to avoid the DataModel in some cases -- and if I could get this to work as a casual observer might think it would, then this would be essentially a one-stop solution for a whole host of things. 


                        I have a number of issues with DataModel -- nothing huge, but I find it difficult to work with in some situations.  One thing that is annoying but easy to deal with is that in this specific case, I need to outject both the main class, as well as create a separate data model for each of its children collections over which I want to be able to iterate on the page.


                        Thanks,
                        ken

                        • 10. Re: Using Seam EL to pass parameters to Action methods - doesn't always work
                          Ken Clark Newbie

                          I appreciate that no one told me to rtfm, which is probably what I deserved.


                          Here is a link to a page that explains this:


                          http://docs.jboss.com/seam/2.0.2.SP1/reference/en-US/html/elenhancements.html


                          And here are some of the most crucial points:




                          It's important to fully understand how this extension to EL works. When the page is rendered, the parameter names are stored (for example, hotel.id and user.username), and evaluated (as value expressions) when the page is submitted. You can't pass objects as parameters!



                          You must ensure that the parameters are available not only when the page is rendered, but also when it is submittedIf the arguments can not be resolved when the page is submitted the action method will be called with null arguments!


                          and, interestingly:




                          Components like c:forEach and ui:repeat iterate over a List or array, exposing each item in the list to nested components. This works great if you are selecting a row using a h:commandButton or h:commandLink



                          However if you want to use s:link or s:button you must expose the items as a DataModel, and use a dataTable (or equivalent from a component set like rich:dataTable). Neither s:link or s:button submit the form (and therefore produce a bookmarkable link) so a magic parameter is needed to recreate the item when the action method is called. This magic parameter can only be added when a data table backed by a DataModel is used


                          So I will see if I can get this working using h:commandLink.  Otherwise, I now see the limitations.


                          Thanks all for the help.


                          ken