0 Replies Latest reply on Jan 22, 2013 4:36 AM by Christophe Noel

    Richfaces 4 + Liferay: tree toggling issue with Ajax

    Christophe Noel Newbie

      Dear all,

       

      I have read all the stuff related to my issue: when switching to Ajax mode, I'm only able to toggle (expand) the first level nodes, but not the second level nodes.

      I found the following related issue (but seem to be closed):

       

      I just created the following issue ticket: RF-12743

       

      Thanks so much to anyone who could help !

       

      I'm using the following configuration:

      • Liferay 6.0.5 on Tomcat v6.0
      • JSF 2.1.3
      • Richfaces 4.2.3 (but also tried with 4.2.2)
      • Liferay Faces Bridge 3.1.0

       

      Note : I first tried to implement my TreeNode by extending the richfaces TreeNodeImpl but, as I got a casting issue (cannot be cast to swing.tree, probably due to a classloading issue but I could find the cause), I finally implemented my own TreeNodeImpl implementing the swing.treenode class (then I got no more problem except the present issue).

       

      The XHTML part (refine.xhtml). It displays a tree on 3 levels (1st level if an offeringNode, 2nd is a propertyNode, 3rd is a fieldNode) :

       

      <rich:collapsiblePanel switchType="client" id="refinePanel" expanded="true">
                          <f:facet name="headerExpanded">
                                    <h:graphicImage value="/images/icons/basket_edit.png" style="vertical-align:middle;"/>
                                    <h:outputText value=" Refinement"/>
                          </f:facet>
                          <f:facet name="headerCollapsed">
                                    <h:graphicImage value="/images/icons/basket_edit.png" style="vertical-align:middle;"/>
                                    <h:outputText value=" Refinement"/>
                          </f:facet>
                          <b> <h:outputText value="Select Requested Fields"/></b>
      
      
                          <rich:tree rendered="#{refineTreeBean.renderable}" id="tree" nodeType="#{node.type}" var="node" value="#{refineTreeBean.rootNodes}" toggleType="ajax">
                                    <rich:treeNode id="offeringNode" type="offering" iconExpanded="/images/icons/folder.png" iconCollapsed="/images/icons/folder.png">
                                              <h:selectBooleanCheckbox value="#{node.selected}">
                                                        <a4j:ajax event="change" render="refinePanel" action="#{refineTreeBean.refineAction()}"/>
                                              </h:selectBooleanCheckbox> 
                          #{node.name} 
                      </rich:treeNode>
                                    <rich:treeNode expanded="#{true}" id="propertyNode" type="property" iconExpanded="/images/icons/textfield_key.png" iconCollapsed="/images/icons/textfield_key.png">
                                              <h:selectBooleanCheckbox value="#{node.selected}">
                                                        <a4j:ajax event="change" render="refinePanel" action="#{refineTreeBean.refineAction()}"/>
                                              </h:selectBooleanCheckbox> 
                          #{node.name} 
                      </rich:treeNode>
                                    <rich:treeNode id="fieldNode" type="field" iconLeaf="/images/icons/textfield.png">
                                              <h:selectBooleanCheckbox value="#{node.selected}">
                                                        <a4j:ajax event="change" render="refinePanel" action="#{refineTreeBean.refineAction()}"/>
                                              </h:selectBooleanCheckbox> 
                         #{node.name} 
                      </rich:treeNode>
                          </rich:tree>
                </rich:collapsiblePanel>
      
      

       

      My RefineTreeBean (view scope). It includes a toggleNodeListener method, because I tried this way but another symptom is that I get an exception (IllegalArgument) on tree.getRowData() :

       

      /**
       * 
       */
      package be.spacebel.ssa.swe.beans;
      
      
      import java.util.ArrayList;
      import java.util.Arrays;
      import java.util.HashMap;
      import java.util.List;
      import java.util.Map;
      
      
      import javax.swing.tree.TreeNode;
      
      
      import org.richfaces.component.UITree;
      import org.richfaces.event.TreeSelectionChangeEvent;
      import org.richfaces.event.TreeToggleEvent;
      
      
      import be.spacebel.ssa.swe.data.FieldNode;
      import be.spacebel.ssa.swe.data.OfferingMetadata;
      import be.spacebel.ssa.swe.data.OfferingNode;
      import be.spacebel.ssa.swe.data.PropertyNode;
      import be.spacebel.ssa.swe.data.SelectableNode;
      
      
      /**
       * @author cnl
       * 
       */
      public class RefineTreeBean {
      
      
                private List<SelectableNode> rootNodes=new ArrayList<SelectableNode>();
                private TreeNode currentSelection = null;
                private be.spacebel.ssa.swe.beans.MetadataBean metadataBean;
                private boolean renderable;
      
                public RefineTreeBean() {
      
                }
      
                 public void toggleNodeListener(TreeToggleEvent ev){
                        UITree tree = (UITree)ev.getSource();
                        SelectableNode nodeExp = ((SelectableNode)tree.getRowData());
                        nodeExp.setExpanded(!nodeExp.isExpanded());
                        System.out.println("The node "+nodeExp.getName()+" has toggled!");
                    }
      
                public void refineAction() {
                          System.out.println("checkbox clickeed");
                }
                public void add(String clickedOffering, ArrayList<String> selectedProps) {
                          this.renderable = true;
      
                                    OfferingMetadata desc = metadataBean.getOfferingDescriptions().get(clickedOffering);
                                    this.rootNodes.add(new OfferingNode(desc,selectedProps));
                }
      
      
                public void remove(String clickedOffering) {
                          for(SelectableNode  node : this.rootNodes){
                                    if(node.getName().equals(clickedOffering)) {
                                              this.rootNodes.remove(node);
                                    }
                          }
      
                          return;
                }
      
      
      public void addAll(ArrayList<String> offerings, ArrayList<String> selectedProps) {
                          this.rootNodes = new ArrayList<SelectableNode>();
      
                          for(String off : offerings) {
                                    OfferingMetadata desc = metadataBean.getOfferingDescriptions().get(off);
                                    this.rootNodes.add(new OfferingNode(desc,selectedProps));
                          }
                          this.renderable = true;
      
                                    //this.rootNodes.add(new OfferingNode(desc));
                }
                public void removeAll() {
      
                          this.renderable = false;
                          this.rootNodes = new ArrayList<SelectableNode>();
                          return;
                }
      
                public void selectionChanged(TreeSelectionChangeEvent selectionChangeEvent) {
              // considering only single selection
                          System.out.println("selection change");
                          /**
              List<Object> selection = new ArrayList<Object>(selectionChangeEvent.getNewSelection());
              Object currentSelectionKey = selection.get(0);
              UITree tree = (UITree) selectionChangeEvent.getSource();
      
              Object storedKey = tree.getRowKey();
              tree.setRowKey(currentSelectionKey);
              currentSelection = (TreeNode) tree.getRowData();
              tree.setRowKey(storedKey);
                                   * 
                           */
      
      
          }
      
                public List<SelectableNode> getRootNodes() {
                          return rootNodes;
                }
      
      
                public void setRootNodes(List<SelectableNode> rootNodes) {
                          this.rootNodes = rootNodes;
                }
      
      
      
      
                public TreeNode getCurrentSelection() {
                          return currentSelection;
                }
      
      
                public void setCurrentSelection(TreeNode currentSelection) {
                          this.currentSelection = currentSelection;
                }
      
      
                public be.spacebel.ssa.swe.beans.MetadataBean getMetadataBean() {
                          return metadataBean;
                }
      
      
                public void setMetadataBean(
                                    be.spacebel.ssa.swe.beans.MetadataBean metadataBean) {
                          this.metadataBean = metadataBean;
                }
      
      
                public boolean isRenderable() {
                          return renderable;
                }
      
      
                public void setRenderable(boolean renderable) {
                          this.renderable = renderable;
                }
      
      }
      
      

       

      An example here of what is an OfferingNode:

       

      public class OfferingNode extends SelectableNode  {
      
      
      
      
                public OfferingNode(OfferingMetadata desc, ArrayList<String> selectedProps) {
                          super("offering", desc.getIdentifier());
                          this.selected=true;
                          for(String  property : desc.getObservablePropertyArray())
                          {
                                    if(selectedProps.size() == 0 || selectedProps.contains(property)) {
                                              addChild(property, new PropertyNode(property,desc,true)); 
                                    }
                                    else
                                    {
                                    addChild(property, new PropertyNode(property,desc,false));
                                    }
      
      
                          }
                                    addChild(SWEUtil.UNQUALIFIED_FIELD, new PropertyNode(SWEUtil.UNQUALIFIED_FIELD,desc,false));
                }
        
      }
      
      

       

      This extends SelectableNode:

       

      package be.spacebel.ssa.swe.data;
      
      
      import java.util.Collection;
      
      
      import javax.swing.tree.TreeNode;
      
      
      import be.spacebel.ssa.swe.util.TreeNodeImpl;
      
      
      
      
      
      
      /**
       * TreeNodeImpl from Richfaces cannot be used because of a class loading problem (probably)
       * But the issue couldn't be fixed (treenode cannot be cast to spring.treenode)
       * Therefore we created a TreeNodeImpl which implements TreeNode from spring
       * @author cnl
       *
       */
      
      
      public class SelectableNode extends TreeNodeImpl {
      
      
      
                /**
                 * Extension of a default TreeNode: this node has a name, and can be selectable
                 * Type give indication on the node type (offering, property or field)
                 */
          protected String type;
          protected String name;
          protected boolean selected=false;
          protected boolean expanded=true;
      
          public SelectableNode(String type, String name) {
                    super();
                    setType(type);
                    setName(name);
                    System.out.println("new Selectable Node");
      
          }
      
      
      
          public boolean isExpanded() {
              return expanded;
          }
      
          public void setExpanded(boolean expanded) {
              this.expanded = expanded;
          }
      
          public boolean isSelected() {
                          return selected;
                }
      
      
                public void setSelected(boolean selected) {
                          System.out.println(getName()+ " is now "+selected);
                          this.selected = selected;
                          if(getChildCount()>0) {
                                    System.out.println("has children");
                                    for(TreeNode node : this.getChildrenMap().values() ) {
                                              SelectableNode selectableNode = (SelectableNode) node;
                                              selectableNode.setSelected(selected);
                                    }
                          }
      
                }
      
      
                public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
              System.out.println(name);
      
          }
      
          public String getType() {
              return type;
          }
      
          public void setType(String type) {
              this.type = type;
          }
      
          @Override
          public String toString() {
              return this.name;
          }
      }
      
      
      

       

      And my TreeNodeImpl:

       

      public class TreeNodeImpl implements TreeNode {
      
      
                protected Map<Object, TreeNode> childrenMap;
      
      
                protected TreeNode parent;
      
      
                public TreeNodeImpl() {
                          this.childrenMap = new HashMap<Object, TreeNode>();
                }
      
      
                @Override
                public Enumeration<TreeNode> children() {
                          return Collections.enumeration(this.childrenMap.values());
                }
      
      
                @Override
                public boolean getAllowsChildren() {
                          // TODO Auto-generated method stub
                          return true;
                }
      
      
                @Override
                public TreeNode getChildAt(int arg0) {
                          return this.childrenMap.get(arg0);
                }
      
      
                @Override
                public int getChildCount() {
                          return this.childrenMap.size();
                }
      
      
                @Override
                public int getIndex(TreeNode arg0) {
                          return (new ArrayList<TreeNode>(this.childrenMap.values())).indexOf(arg0);
                }
      
      
                @Override
                public TreeNode getParent() {
                          return this.parent;
                }
      
      
                public void setParent(TreeNode parent) {
                          this.parent = parent;
                          return;
                }
      
      
                public void addChild(Object identifier, TreeNodeImpl child) {
                          this.childrenMap.put(identifier, child);
                          child.setParent(this);
                }
      
                /**
                 * remove
                 public void addChildCond(Object identifier, TreeNodeImpl child) {
                          if(!this.childrenMap.containsKey(identifier)) {
                                    addChild(identifier, child);
                          }
      
                }
      */
      
                public Map<Object, TreeNode> getChildrenMap() {
                          return childrenMap;
                }
      
      
                public void setChildrenMap(Map<Object, TreeNode> children) {
                          this.childrenMap = children;
                }
      
      
                @Override
                public boolean isLeaf() {
                          // TODO Auto-generated method stub
                          return (this.childrenMap.size() == 0);
                }
      
      
      }
      
      

       

      The richfaces log is as follow when I try to expand the second level node :

      debug[10:20:17.809]: New request added to queue. Queue requestGroupingId changed to A1568:uiSearchForm:tree:0.0:propertyNode
      debug[10:20:17.810]: Queue will wait 0ms before submit
      debug[10:20:17.814]: richfaces.queue: will submit request NOW
      info [10:20:17.816]: Received 'begin' event from <div id=A1568:uiSearchForm:tree:0.0:propertyNode class="rf-tr-nd" rf-tr-nd-colps ...>
      info [10:20:17.869]: Received 'beforedomupdate' event from <div id=A1568:uiSearchForm:tree:0.0:propertyNode class="rf-tr-nd" rf-tr-nd-colps ...>
      debug[10:20:17.870]: Server returned responseText: <partial-response><changes><update id="A1568:uiSearchForm:j_idt13"><![CDATA[<span id="A1568:uiSearchForm:j_idt13"> <div id="messageD"> <table id="messageT" class="rich-messages"> </table> </div></span>]]></update><update id="A1568:uiSearchForm:tree:0.0:propertyNode"><![CDATA[<div class="rf-tr-nd rf-tr-nd-colps" id="A1568:uiSearchForm:tree:0.0:propertyNode"><div class="rf-trn"><span class="rf-trn-hnd-colps rf-trn-hnd"></span><span class="rf-trn-cnt"><img class="rf-trn-ico-colps rf-trn-ico rf-trn-ico-cst" alt="" src="/LiferayTest-portlet/images/icons/textfield_key.png" /><span class="rf-trn-lbl"><input id="A1568:uiSearchForm:tree:0.0:j_idt87" type="checkbox" name="A1568:uiSearchForm:tree:0.0:j_idt87" checked="checked" onchange="RichFaces.ajax(this,event,{&quot;parameters&quot;:{&quot;javax.faces.behavior.event&quot;:&quot;change&quot;,&quot;org.richfaces.ajax.component&quot;:&quot;A1568:uiSearchForm:tree:0.0:j_idt87&quot;} ,&quot;sourceId&quot;:this} )" /> timeproperty </span></span></div></div>]]></update><eval><![CDATA[RichFaces.ui.TreeNode.initNodeByAjax("A1568:uiSearchForm:tree:0.0:propertyNode",{"clientEventHandlers":{} } )]]></eval><update id="javax.faces.ViewState"><![CDATA[7381444013844425114:-1903004591067256073]]></update></changes></partial-response>
      info [10:20:17.871]: Listing content of response changes element:
      Element update for id=A1568:uiSearchForm:j_idt13
      <update id="A1568:uiSearchForm:j_idt13"><![CDATA[<span id="A1568:uiSearchForm:j_idt13"> <div id="messageD"> <table id="messageT" class="rich-messages"> </table> </div></span>]]></update>
      Element update for id=A1568:uiSearchForm:tree:0.0:propertyNode
      <update id="A1568:uiSearchForm:tree:0.0:propertyNode"><![CDATA[<div class="rf-tr-nd rf-tr-nd-colps" id="A1568:uiSearchForm:tree:0.0:propertyNode"><div class="rf-trn"><span class="rf-trn-hnd-colps rf-trn-hnd"></span><span class="rf-trn-cnt"><img class="rf-trn-ico-colps rf-trn-ico rf-trn-ico-cst" alt="" src="/LiferayTest-portlet/images/icons/textfield_key.png" /><span class="rf-trn-lbl"><input id="A1568:uiSearchForm:tree:0.0:j_idt87" type="checkbox" name="A1568:uiSearchForm:tree:0.0:j_idt87" checked="checked" onchange="RichFaces.ajax(this,event,{&quot;parameters&quot;:{&quot;javax.faces.behavior.event&quot;:&quot;change&quot;,&quot;org.richfaces.ajax.component&quot;:&quot;A1568:uiSearchForm:tree:0.0:j_idt87&quot;} ,&quot;sourceId&quot;:this} )" /> timeproperty </span></span></div></div>]]></update>
      Element eval
      <eval><![CDATA[RichFaces.ui.TreeNode.initNodeByAjax("A1568:uiSearchForm:tree:0.0:propertyNode",{"clientEventHandlers":{} } )]]></eval>
      Element update for id=javax.faces.ViewState
      <update id="javax.faces.ViewState"><![CDATA[7381444013844425114:-1903004591067256073]]></update>
      
      debug[10:20:17.873]: richfaces.queue: ajax submit successfull
      debug[10:20:17.873]: richfaces.queue: Nothing to submit
      info [10:20:17.873]: Received 'success' event from <div id=A1568:uiSearchForm:tree:0.0:propertyNode class="rf-tr-nd" rf-tr-nd-colps ...>
      info [10:20:17.873]: Received 'complete' event from <div id=A1568:uiSearchForm:tree:0.0:propertyNode class="rf-tr-nd" rf-tr-nd-colps ...>
      
      
      
      
      
      
      
      
      
      
      
      
      
      

       

      Please find attached the java sources (but off course including a lot of other stuff). Thanks,

       

      Christophe.