10 Replies Latest reply on Mar 28, 2008 2:59 PM by Stan Silvert

    h:commandLink whith f:param don't post param.

    Alex M Newbie

      Hi,

      i use tomcat 6.0.13 with jsf 1.2_04-b16-p02 and richfaces 3.1.4.
      i have h:commandLink whith f:param

      <h:form id="client_form">
       <rich:dataList value="#{mb_sessionOrganizationList.organizations}" var="orgs" id="client_list">
       <f:facet name="header">
       <h:outputText value="#{rb_labels.lblCompanyName}"/>
       </f:facet>
       <h:commandLink id="org" value="#{orgs.caption}" action="#{mb_sessionOrganizationList.doSelect}">
       <f:param id="client_id" name="client_id" value="#{orgs.id}"/>
       </h:commandLink>
       </rich:dataList>
      </h:form>
      


      result html code is
      <form id="client_form" name="client_form" method="post" action="/pilot-0.2-web-v2/pages/SelectOrganization.jsf" enctype="application/x-www-form-urlencoded">
       <input type="hidden" name="client_form" value="client_form"/>
       <ul class="dr-list rich-datalist " id="client_form:client_list">
       <li id="client_form:client_list:0" class="dr-list-item rich-list-item ">
       <script type="text/javascript" language="Javascript">
      //<![CDATA[
      function dpf(f) {var adp = f.adp;if (adp != null) {for (var i = 0;i < adp.length;i++) {f.removeChild(adp);}}};function apf(f, pvp) {var adp = new Array();f.adp = adp;var ps = pvp.split(',');for (var i = 0,ii = 0;i < ps.length;i++,ii++) {var p = document.createElement("input");p.type = "hidden";p.name = ps;p.value = ps[i + 1];f.appendChild(p);adp[ii] = p;i += 1;}};function jsfcljs(f, pvp, t) {apf(f, pvp);var ft = f.target;if (t) {f.target = t;}f.submit();f.target = ft;dpf(f);};
      //]]></script>
       <a id="client_form:client_list:0:org" href="#" onclick="if(typeof jsfcljs == 'function'){jsfcljs(document.forms['client_form'],'client_form:client_list:0:org,client_form:client_list:0:org,client_id,2','');}return false">001</a>
       </li>
       </ul>
       <input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="smth long"/>
      </form>
      


      if i try to use clickCommandLink then no param post (java script don't execute).
      if i try ajaxSubmit then parameter posts, but navigation rule don't work (outcome is success).
      faces.config fragment here:
       <navigation-rule>
       <from-view-id>/pages/SelectOrganization.xhtml</from-view-id>
       <navigation-case>
       <from-outcome>success</from-outcome>
       <to-view-id>/pages/MainView.xhtml</to-view-id>
       <redirect />
       </navigation-case>
       <navigation-case>
       <from-outcome>failure</from-outcome>
       <to-view-id>/pages/SelectOrganization.xhtml</to-view-id>
       </navigation-case>
       </navigation-rule>
      


      i tried this with beta1 and with last svn - no difference.

      anyone have any idea's how i can test commandLink whith param?

        • 1. Re: h:commandLink whith f:param don't post param.
          Stan Silvert Master

          That's a bug. Should be easy to fix. I'll let you know soon.

          Stan

          • 2. Re: h:commandLink whith f:param don't post param.
            Alex M Newbie

            Ok. Thanx for fast reply.

            • 3. Re: h:commandLink whith f:param don't post param.
              Stan Silvert Master

              This should be working now. Give it a try from the code in SVN.

              Jira reference:
              http://jira.jboss.com/jira/browse/JSFUNIT-81

              Stan

              • 4. Re: h:commandLink whith f:param don't post param.
                Alex M Newbie

                there is no effect.

                maybe cause of this is in value binding through iterable variable?

                • 5. Re: h:commandLink whith f:param don't post param.
                  Stan Silvert Master

                   

                  "cybeer" wrote:
                  maybe cause of this is in value binding through iterable variable?


                  Arg. I overlooked the iterable variable. That makes it much harder. I'll have to parse the params from the javascript.

                  Thanks for your patience.

                  Stan

                  • 6. Re: h:commandLink whith f:param don't post param.
                    Alex M Newbie

                    what do you think abount solution like this? its only need updating for complex EL exspressions, imho.

                     private void setFParams(WebRequest req, String componentID)
                     throws SAXException, IOException
                     {
                    
                     Log log = LogFactory.getLog(this.getClass());
                    
                     JSFServerSession server = new JSFServerSession(this);
                    
                     UIComponent cmdLink = server.findComponent(componentID);
                    
                     if (cmdLink.getChildCount() == 0) return;
                    
                     UIComponent child;
                     UIData dataParent;
                     String dataVar, dataExpr = "", paramExpr = "", finalExpr;
                     int rowIndex = -1, varIndex = -1;
                     ValueExpression expr;
                     for (Iterator<UIComponent> i = cmdLink.getChildren().iterator(); i.hasNext();)
                     {
                     child = i.next();
                    
                    
                     if (!child.isRendered()) continue;
                    
                     if (child instanceof UIParameter)
                     {
                     UIParameter uiParam = (UIParameter)child;
                     dataParent = getUIDataParent(child);
                     paramExpr = child.getValueExpression("value").getExpressionString();
                     finalExpr = paramExpr;
                     if(dataParent != null){
                     dataVar = dataParent.getVar();
                     rowIndex = getRowIndex(dataParent, child, server.getFacesContext());
                     varIndex = paramExpr.indexOf(dataVar);
                     if( varIndex != -1 && rowIndex != -1){
                     dataExpr = dataParent.getValueExpression("value").getExpressionString();
                     finalExpr = dataExpr.substring(2, dataExpr.length() - 1) + "[" +
                     Integer.toString(rowIndex) + "]";
                     finalExpr = paramExpr.replace(dataVar, finalExpr);
                     }
                     }
                    
                     log.debug("dataParent " + dataParent);
                     log.debug("paramExpr " + paramExpr);
                     log.debug("finalExpr " + finalExpr);
                     log.debug("rowIndex " + rowIndex);
                     log.debug("varIndex " + varIndex);
                     log.debug("dataExpr " + dataExpr);
                    /* log.debug("set param " + uiParam.getName() + " to val "+ (String)uiParam.getValue());
                    
                     log.debug("param value expr " + uiParam.getValueExpression("value").getValue(facesContext.getELContext()) );
                     log.debug("expr " + uiParam.getValueExpression("value").getExpressionString());*/
                     expr = server.getFacesContext().getApplication().getExpressionFactory()
                     .createValueExpression(server.getFacesContext().getELContext(),
                     finalExpr, String.class);
                     req.setParameter(uiParam.getName(), (String)expr.getValue(server.getFacesContext().getELContext()));
                     }
                     }
                     }
                    
                     private UIData getUIDataParent(UIComponent comp){
                    
                     if(comp.getParent() == null)
                     return null;
                     if(comp.getParent() instanceof UIData)
                     return (UIData)comp.getParent();
                     return getUIDataParent(comp.getParent());
                    
                     }
                    
                     private int getRowIndex(UIData parent, UIComponent child, FacesContext context){
                    
                     List<UIComponent> listChildren = parent.getChildren();
                     UIComponent curComp;
                     for(int i = 0; i < listChildren.size(); i++){
                     curComp = listChildren.get(i);
                     if(clientIDs.isAncestor(child.getClientId(context), curComp.getClientId(context)))
                     return i;
                     }
                     return -1;
                     }
                    
                    


                    • 7. Re: h:commandLink whith f:param don't post param.
                      Stan Silvert Master

                      Nice.

                      The only other problem I see is that you are using some JSF 1.2 classes and we are still supporting JSF 1.1.

                      If we want to stick with a server-side solution, we can help things a bit by saving the row index in ClientIDs for those components that have them. Then we should be able to call UIComponent.setRowIndex() and restore the row before calling UIParameter.getValue(). That way, you let JSF do more of the work for you.

                      But parsing the javascript to get the param values is not very hard and it's a lot less code. The only problem I see is that you need a different parser for MyFaces and the RI. Plus it could break in future JSF versions. But we would know it right away because JSFUnit's own unit tests would catch it.

                      Anyway, just thinking out loud here. I'm going to come back to this soon. I'd love to hear if you have any more comments.

                      Stan

                      • 8. Re: h:commandLink whith f:param don't post param.
                        Alex M Newbie

                        if support fo jsf 1.1 is necessary then deprecated classes from javax.faces.el can be used. like this:

                         private void setFParams(WebRequest req, String componentID)
                         throws SAXException, IOException
                         {
                        
                         Log log = LogFactory.getLog(this.getClass());
                        
                         JSFServerSession server = new JSFServerSession(this);
                         UIComponent cmdLink = server.findComponent(componentID);
                        
                         if (cmdLink.getChildCount() == 0) return;
                        
                         UIComponent child;
                         UIData dataParent;
                         String dataVar, dataExpr = "", paramExpr = "", finalExpr;
                         int rowIndex = -1, varIndex = -1;
                         ValueBinding expr;
                         for (Iterator<UIComponent> i = cmdLink.getChildren().iterator(); i.hasNext();)
                         {
                         child = i.next();
                        
                        /* log.debug("child class = " + child.getClass());
                         log.debug("child isRendered = " + child.isRendered());*/
                        
                         if (!child.isRendered()) continue;
                        
                         if (child instanceof UIParameter)
                         {
                         UIParameter uiParam = (UIParameter)child;
                         dataParent = getUIDataParent(child);
                         paramExpr = child.getValueBinding("value").getExpressionString();
                         finalExpr = paramExpr;
                         if(dataParent != null){
                         dataVar = dataParent.getVar();
                         rowIndex = getRowIndex(dataParent, child, server.getFacesContext());
                         varIndex = paramExpr.indexOf(dataVar);
                         if( varIndex != -1 && rowIndex != -1){
                         dataExpr = dataParent.getValueBinding("value").getExpressionString();
                         finalExpr = dataExpr.substring(2, dataExpr.length() - 1) + "[" +
                         Integer.toString(rowIndex) + "]";
                         finalExpr = paramExpr.replace(dataVar, finalExpr);
                         }
                         }
                        
                         log.debug("dataParent " + dataParent);
                         log.debug("paramExpr " + paramExpr);
                         log.debug("finalExpr " + finalExpr);
                         log.debug("rowIndex " + rowIndex);
                         log.debug("varIndex " + varIndex);
                         log.debug("dataExpr " + dataExpr);
                         expr = server.getFacesContext().getApplication().createValueBinding(finalExpr);
                        /* .createValueExpression(server.getFacesContext().getELContext(),
                         finalExpr, String.class);*/
                         req.setParameter(uiParam.getName(),
                         (String)expr.getValue(server.getFacesContext()));
                         }
                         }
                         }
                        


                        If we want to stick with a server-side solution, we can help things a bit by saving the row index in ClientIDs for those components that have them. Then we should be able to call UIComponent.setRowIndex() and restore the row before calling UIParameter.getValue(). That way, you let JSF do more of the work for you.

                        this way means some injections in jsf renderer, isn't it? i don't know how to get row indexes of UIData from outside the renderer :(


                        But parsing the javascript to get the param values is not very hard and it's a lot less code. The only problem I see is that you need a different parser for MyFaces and the RI. Plus it could break in future JSF versions. But we would know it right away because JSFUnit's own unit tests would catch it.

                        a4j:commandLink needs parser too ;)

                        i think server side solution must be less framework-depended than client-side alternative. and so more universal.

                        • 9. Re: h:commandLink whith f:param don't post param.
                          Alex M Newbie

                          one more problem was detected. if there is rich:tree on page then throws TransformerException.

                          java.lang.RuntimeException: javax.xml.transform.TransformerException: org.w3c.dom.DOMException: NAMESPACE_ERR: An attempt is made to create or change an object in a way which is incorrect with regard to namespaces.
                          at org.jboss.jsfunit.facade.JSFClientSession.updateInternalState(JSFClientSession.java:225)
                          at org.jboss.jsfunit.facade.JSFClientSession.doWebRequest(JSFClientSession.java:197)
                          at org.jboss.jsfunit.facade.JSFClientSession.clickCommandLink(JSFClientSession.java:473)
                          at ru.rfc.tests.LoginTest.testLogin(LoginTest.java:70)
                          at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                          at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
                          at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
                          at org.apache.cactus.internal.AbstractCactusTestCase.runBareServer(AbstractCactusTestCase.java:153)
                          at org.apache.cactus.internal.server.AbstractWebTestCaller.doTest(AbstractWebTestCaller.java:119)
                          at org.apache.cactus.internal.server.AbstractWebTestController.handleRequest_aroundBody0(AbstractWebTestController.java:93)
                          at org.apache.cactus.internal.server.AbstractWebTestController.handleRequest_aroundBody1$advice(AbstractWebTestController.java:224)
                          at org.apache.cactus.internal.server.AbstractWebTestController.handleRequest(AbstractWebTestController.java)
                          at org.apache.cactus.server.ServletTestRedirector.doPost_aroundBody2(ServletTestRedirector.java:101)
                          at org.apache.cactus.server.ServletTestRedirector.doPost_aroundBody3$advice(ServletTestRedirector.java:224)
                          at org.apache.cactus.server.ServletTestRedirector.doPost(ServletTestRedirector.java)
                          at org.apache.cactus.server.ServletTestRedirector.doGet_aroundBody0(ServletTestRedirector.java:72)
                          at org.apache.cactus.server.ServletTestRedirector.doGet_aroundBody1$advice(ServletTestRedirector.java:224)
                          at org.apache.cactus.server.ServletTestRedirector.doGet(ServletTestRedirector.java)
                          at javax.servlet.http.HttpServlet.service(HttpServlet.java:690)
                          at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
                          at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
                          at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
                          at org.jboss.jsfunit.framework.JSFUnitFilter.doFilter(JSFUnitFilter.java:122)
                          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:230)
                          at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
                          at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
                          at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104)
                          at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
                          at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:261)
                          at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
                          at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:581)
                          at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
                          at java.lang.Thread.run(Unknown Source)
                          Caused by: javax.xml.transform.TransformerException: org.w3c.dom.DOMException: NAMESPACE_ERR: An attempt is made to create or change an object in a way which is incorrect with regard to namespaces.
                          at org.apache.xalan.transformer.TransformerIdentityImpl.transform(TransformerIdentityImpl.java:401)
                          at org.jboss.jsfunit.facade.DOMUtil.convertToDomLevel2(DOMUtil.java:143)
                          at org.jboss.jsfunit.facade.JSFClientSession.updateInternalState(JSFClientSession.java:216)
                          ... 37 more
                          Caused by: org.w3c.dom.DOMException: NAMESPACE_ERR: An attempt is made to create or change an object in a way which is incorrect with regard to namespaces.
                          at org.apache.xml.utils.DOMBuilder.startElement(DOMBuilder.java:401)
                          at org.apache.xalan.transformer.TransformerIdentityImpl.startElement(TransformerIdentityImpl.java:1073)
                          at org.apache.xml.serializer.TreeWalker.startNode(TreeWalker.java:359)
                          at org.apache.xml.serializer.TreeWalker.traverse(TreeWalker.java:145)
                          at org.apache.xalan.transformer.TransformerIdentityImpl.transform(TransformerIdentityImpl.java:390)
                          ... 39 more
                          


                          generated html fragment:
                          <div class="dr-tree rich-tree " id="navigationTree" xmlns:rich="http://richfaces.ajax4jsf.org/rich">
                           <div id="navigationTree:childs">
                           <p class="dr-tree-last-node-marker"/>
                           <table border="0" cellpadding="0" cellspacing="0" class="dr-tree-full-width rich-tree-node" id="navigationTree:1:j_id7">
                           <tbody>
                           <tr id="navigationTree:1:j_id7:mainRow" onclick=" ">
                           <td class="dr-tree-h-ic rich-tree-node-handleicon dr-tree-h-ic-line-last">
                           <div>
                           <input class="dr-tree-h-input" id="navigationTree:1:j_id7NodeExpanded" name="navigationTree:1:j_id7NodeExpanded" type="hidden" value="false"/>
                           <a href="#" id="navigationTree:1:j_id7:handle" onclick=" return false;">
                           <img border="0" class="dr-tree-pointer-cursor dr-tree-h-ic-img-md dr-tree-h-ic-img rich-tree-node-handleicon-collapsed" id="navigationTree:1:j_id7:handle:img:collapsed" src="/pilot-0.2-web-v2/a4j_3_1_4.GAorg.richfaces.renderkit.html.images.TreePlusImage/DATB/eAH7....1Tv3GRg
                          YACS3BY4_.jsf"/>
                           <img border="0" class="dr-tree-pointer-cursor dr-tree-h-ic-img-md dr-tree-h-ic-img rich-tree-node-handleicon-expanded" id="navigationTree:1:j_id7:handle:img:expanded" src="/pilot-0.2-web-v2/a4j_3_1_4.GAorg.richfaces.renderkit.html.images.TreeMinusImage/DATB/eAH7....1Tv3GR
                          gYACS3BY4_.jsf" style="display: none;"/>
                           </a>
                           </div>
                           </td>
                           <td class="dr-tree-h-ic rich-tree-node-icon dr-tree-h-ic-line-clp" id="navigationTree:1:j_id7:icon" rich:draggableoptions="{'parameters':{'dragSourceId':'navigationTree:1:j_id7','navigationTree:1:j_id7':'navigationTree:1:j_
                          id7'} } " rich:dropzoneoptions="{} ">
                           <img class="dr-tree-h-ic-img-md dr-tree-h-ic-img" src="/pilot-0.2-web-v2/a4j_3_1_4.GAimages/iconFolder.gif.jsf"/>
                           </td>
                           <td class="dr-tree-h-text rich-tree-node-text " id="navigationTree:1:j_id7:text" rich:highlightedclass="dr-tree-i-hl rich-tree-node-highlighted" rich:selectedclass="dr-tree-i-sel rich-tree-node-selected">TestGroup_1205250227671</td>
                           </tr>
                           </tbody>
                           </table>
                           <div id="navigationTree:1:j_id7:childs" style="display: none;" class="dr-tree-layout-on dr-tree-h-ic-div rich-tree-node-children rich-tree-node-cildren"/>
                           </div>
                           <input type="hidden" id="navigationTree:input" name="navigationTree:input" value=""/>
                           <script type="text/javascript">
                          
                          //<![CDATA[
                          var Richfaces_Tree_navigationTree =
                           new Tree("navigationTree", "navigationTree:input", "client",
                           {
                           onselect: "",
                           onexpand: "",
                           oncollapse: ""
                           },
                           function(event) {
                           A4J.AJAX.Submit('_viewRoot',null,event,{'parameters':{'navigationTree:selectedNode':event.selecte
                          dNode} ,'actionUrl':'/pilot-0.2-web-v2/pages/MainView.jsf?javax.portlet.faces.DirectLink=true'} ); r
                          eturn false;
                           },
                           false,
                           true
                           );
                           Richfaces_Tree_navigationTree.drop = function(event,drag){var options = {'parameters':{'navigation
                          Tree':'navigationTree'} ,'actionUrl':'/pilot-0.2-web-v2/pages/MainView.jsf?javax.portlet.faces.Direc
                          tLink=true'} ;options.parameters['dropTargetId'] = 'navigationTree';Object.extend(options.parameters
                          ,drag.getParameters());var dzOptions = this.getDropzoneOptions(); if (dzOptions.ondrop) { if (!dzOpt
                          ions.ondrop.call(this, event)) return; };A4J.AJAX.Submit('_viewRoot',null,event,options);};
                          //]]></script>
                           <div id="navigationTree:script">
                           <script type="text/javascript"/>
                           </div>
                          </div>
                          


                          probably cause is in elements with "rich" namespace (e.g. rich:draggableoptions).

                          • 10. Re: h:commandLink whith f:param don't post param.
                            Stan Silvert Master

                            I went ahead and committed a fix for the loop variable problem for Mojarra (JSF RI 1.2). Your clickCommandLink() should now send all of the params it needs.

                            I ended up parsing the params from the javascript call on the client side. I'm not sure that's the best way, but it seemed to be the least complicated for now.

                            Stan