5 Replies Latest reply on Jun 9, 2008 6:11 AM by Bernard Labno

    Two rich:trees in one form

    Bernard Labno Master

      Hello,

      I am experiencing problems with selecting nodes of two trees nested within single form. Can trees be nested in the same form ?

        • 1. Re: Two rich:trees in one form
          Bjoern Eickvonder Newbie

          My app does so, no problem so far, what is your problem more precisely.

          • 2. Re: Two rich:trees in one form
            Bernard Labno Master

            I have lots of problems. When I click on leafA in treeA then on leafB in treeB and again on leafA in treeA then processSelection is not fired. However if I collapse or expand leafA then I can click it and processSelection is fired.
            Also another thing I do not understand is that if processSelection listener method influences nodeFace, and when nodeFace changes, than I cannot expend or collapse node I clicked (seems to be rerender problem, cause rerendering tree after each selection helps).

            Here are my related posts :



            Anyway, it took me 2-3 weeks to implement rich:tree into my project and it was a pain.

            Thanks for attention.

            • 3. Re: Two rich:trees in one form
              Ilya Shaikovsky Master

              I've tested the two trees in one form. Clicking the nodes one by one in these trees caused listener to be called for both trees in my case.

              Share sample war.

              • 4. Re: Two rich:trees in one form
                Bernard Labno Master

                My app is in seam, so it is too big to share it and it would take me too much time to create sample app (i tried and got stupid dependencies problems, which i cant solve quickly) but here is my facelet and backing bean :

                <?xml version='1.0' encoding='UTF-8' ?>
                <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
                <ui:composition xmlns="http://www.w3.org/1999/xhtml"
                 xmlns:ui="http://java.sun.com/jsf/facelets"
                 xmlns:h="http://java.sun.com/jsf/html"
                 xmlns:rich="http://richfaces.org/rich"
                 xmlns:a4j="http://richfaces.org/a4j"
                 xmlns:s="http://jboss.com/products/seam/taglib"
                 xmlns:f="http://java.sun.com/jsf/core"
                 template="/WEB-INF/templates/adminTemplate.xhtml">
                
                 <ui:define name="content">
                 <h:form>
                 <a4j:outputPanel id="trees">
                 <a4j:outputPanel ajaxRendered="true">
                 <h:messages/>
                 </a4j:outputPanel>
                
                 <a4j:status startText="Request in progress..." stopText="Request finished"/>
                 <table id="editOfferCategory">
                 <tr>
                 <td>
                 #{systemTree.value.data.name}
                 <rich:tree id="systemTree"
                 nodeSelectListener="#{categoryManager.processSelection}"
                 ajaxSubmitSelection="true"
                 switchType="ajax"
                 binding="#{systemTree}"
                 var="item"
                 nodeFace="#{categoryManager.getNodeFace(item)}"
                 adviseNodeSelected="#{categoryManager.adviseNodeSelected}"
                 reRender="systemTree"
                 >
                 <rich:treeNode type="unselected" onmouseup="showButton('saveButton')">
                 #{item.name}
                
                 </rich:treeNode>
                 <rich:treeNode type="selected" nodeClass="categorySelected" onmouseup="showButton('saveButton')">
                 #{item.name}
                
                 </rich:treeNode>
                 </rich:tree>
                
                 </td>
                 <td class="buttons">
                 <a4j:commandButton value="#{messages.map}" action="#{categoryManager.mapCategory}" reRender="trees"/>
                 <br />
                 <h:outputLink value="#" onmouseup="showButton('addButton')">
                 #{messages.add}
                 <rich:componentControl for="categoryProperties" operation="show" event="onclick"/>
                 </h:outputLink>
                 <br />
                 <h:commandLink action="#{categoryManager.removeCategory()}">#{messages.remove}</h:commandLink>
                 <br />
                 <h:inputText value="#{categoryName}"/><br />
                
                 <a4j:commandButton value="#{messages.save}" action="#{categoryManager.saveCategory}" id="saveButton" reRender="trees">
                
                 </a4j:commandButton>
                 <a4j:commandButton value="#{messages.add}" action="#{categoryManager.addCategory}" id="addButton" reRender="trees">
                
                 </a4j:commandButton>
                 </td>
                 <td>
                 <!--
                 nodeFace="#{item.getMapping().size() == 0 ? 'notMapped' : 'mapped'}"
                 -->
                 #{externalTree.value.data.name}
                 <rich:tree id="externalTree"
                 nodeSelectListener="#{categoryManager.processSelection}"
                 ajaxSubmitSelection="true"
                 switchType="ajax"
                 binding="#{externalTree}"
                 var="item"
                 nodeFace="#{categoryManager.getNodeFace(item)}"
                 adviseNodeSelected="#{categoryManager.adviseNodeSelected}"
                 onclick="selectExternalTree()"
                 reRender="externalTree"
                 >
                
                 <rich:treeNode type="notMapped" nodeClass="notMapped" onmouseup="showButton('saveButton')">
                 #{item.name}
                
                 </rich:treeNode>
                 <rich:treeNode type="mapped" nodeClass="mapped" onmouseup="showButton('saveButton')">
                 #{item.name}
                
                 </rich:treeNode>
                 <rich:treeNode type="selected" nodeClass="categorySelected" onmouseup="showButton('saveButton')">
                 #{item.name}
                
                 </rich:treeNode>
                 </rich:tree>
                
                 </td>
                 </tr>
                 </table>
                 </a4j:outputPanel>
                 <a4j:outputPanel ajaxRendered="true">
                 <h:outputText value="Selected category : #{categoryName}"/>
                 </a4j:outputPanel>
                 </h:form>
                
                
                 </ui:define>
                
                 </ui:composition>
                
                
                


                @Stateful
                @Name("categoryManager")
                public class CategoryManagerBean implements CategoryManagerLocal {
                
                 private int childrenIdSequence = 1;
                 @In
                 private EntityManager em;
                 @Logger
                 private Log log;
                 @DataModel
                 private List<OfferCategory> rootCategories;
                 @DataModel
                 private List<Class> categoryClasses;
                 @DataModelSelection("categoryClasses")
                 private Class categoryClass;
                 @Out(required = false)
                 private UITree systemTree;
                 @Out(required = false)
                 private UITree externalTree;
                 private ExternalOfferCategory selectedExternalCategory;
                 private SystemOfferCategory selectedSystemCategory;
                 @In
                 private FacesMessages facesMessages;
                 @In(required = false)
                 @Out(required = false)
                 private String categoryName;
                 @Out(required = false)
                 private OfferCategory selectedCategory;
                 private Map<OfferCategory, TreeNode> categoriesNodes = new HashMap<OfferCategory, TreeNode>();
                
                 @Factory(value = "rootCategories")
                 public void loadRootCategories() {
                 rootCategories = em.createNamedQuery(OfferCategory.GET_ROOT_CATEGORIES_QUERY).getResultList();
                 }
                
                 @Factory(value = "categoryClasses")
                 public void loadCategoryClasses() {
                 categoryClasses = new ArrayList<Class>();
                 categoryClasses.add(JojoOfferCategory.class);
                 categoryClasses.add(SystemOfferCategory.class);
                 }
                
                 @Begin(pageflow = "addRootCategory", flushMode = FlushModeType.MANUAL)
                 public void addRootCategory() {
                
                 }
                
                 public void prepareTrees() {
                 if (categoryClass == null) {
                 facesMessages.addFromResourceBundle("selectClassFirst");
                 return;
                 }
                 if (!categoryClass.equals(SystemOfferCategory.class)) {
                 externalTree = getTree(categoryClass);
                 }
                 systemTree = getTree(SystemOfferCategory.class);
                 }
                
                 @Remove
                 public void remove() {
                 }
                
                 private MyTreeNodeImpl asTreeNode(OfferCategory category) {
                 MyTreeNodeImpl node = new MyTreeNodeImpl(generateNodeId());
                 node.setData(category);
                 categoriesNodes.put(category, node);
                 log.debug("added TreeNode with id #0 containing category #1, hashCode=#2", node.getId(), category, category.hashCode());
                 for (OfferCategory subCategory : category.getSubcategories()) {
                 node.addChild(asTreeNode(subCategory));
                 }
                 return node;
                 }
                
                 private UITree getTree(Class<? extends OfferCategory> aClass) {
                 for (OfferCategory category : rootCategories) {
                 if (category.getClass().equals(aClass)) {
                 UITree tree = new HtmlTree();
                 MyTreeNodeImpl node = new MyTreeNodeImpl(generateNodeId());
                 node.addChild(asTreeNode(category));
                 tree.setValue(node);
                 return tree;
                 }
                 }
                 return null;
                 }
                
                 public void processSelection(NodeSelectedEvent e) {
                 log.debug("processSelection");
                 UITree processedTree = (UITree) e.getComponent();
                 selectedCategory = null;
                 try {
                 selectedCategory = (OfferCategory) processedTree.getRowData();
                 categoryName = selectedCategory.getName();
                 } catch (IllegalStateException ex) {
                // log.debug(ex, ex);
                 selectedCategory = null;
                 categoryName = "";
                 }
                 if(processedTree.getId().equals(systemTree.getId())) {
                 selectedSystemCategory = (SystemOfferCategory) selectedCategory;
                 } else {
                 selectedExternalCategory = (ExternalOfferCategory) selectedCategory;
                 }
                // ((TreeState)processedTree.getComponentState()).setSelected(null);
                
                 log.debug("processSelection done");
                 }
                
                 public void selectExternalTree() {
                 selectedCategory = selectedExternalCategory;
                 }
                
                 public void addCategory() {
                 if (selectedCategory == null) {
                 facesMessages.addFromResourceBundle("selectParentCategoryFirst");
                 return;
                 }
                 TreeNode selectedNode = categoriesNodes.get(selectedCategory);
                 OfferCategory newCategory = null;
                 try {
                 newCategory = selectedCategory.getClass().newInstance();
                 } catch (Exception e) {
                 log.error(e);
                 facesMessages.addFromResourceBundle("Technical problem during creation of new category");
                 return;
                 }
                 newCategory.setName(categoryName);
                 newCategory.setParent(selectedCategory);
                 MyTreeNodeImpl newNode = new MyTreeNodeImpl(childrenIdSequence++);
                 newNode.setData(newCategory);
                 selectedNode.addChild(newNode.getId(), newNode);
                 expandNode(getSelectedTree(), selectedNode);
                 categoriesNodes.put(newCategory, newNode);
                 log.debug("added TreeNode with id #0 containing category #1, hashCode=#2", newNode.getId(), newCategory, newCategory.hashCode());
                 Contexts.removeFromAllContexts("categoryName");
                 categoryName = selectedCategory.getName();
                 }
                
                 public void saveCategory() {
                 if (selectedCategory == null) {
                 facesMessages.addFromResourceBundle("selectCategoryFirst");
                 return;
                 }
                 if (categoryName == null || categoryName.trim().equals("")) {
                 facesMessages.addFromResourceBundle("enterCategoryNameFirst");
                 return;
                 }
                 /**
                 * Change of name will affect hashCode, so categories map needs to be updated
                 */
                 TreeNode selectedNode = categoriesNodes.remove(selectedCategory);
                 selectedCategory.setName(categoryName);
                 categoriesNodes.put(selectedCategory, selectedNode);
                 }
                
                 public void removeCategory() {
                 if (selectedCategory == null) {
                 facesMessages.addFromResourceBundle("selectCategoryFirst");
                 return;
                 }
                 MyTreeNodeImpl selectedNode = (MyTreeNodeImpl) categoriesNodes.remove(selectedCategory);
                 selectedNode.getParent().removeChild(selectedNode.getId());
                 if (selectedCategory.equals(selectedSystemCategory)) {
                 selectedSystemCategory = null;
                 } else {
                 selectedExternalCategory = null;
                 }
                 /**
                 * category cannot be modified before node is retrived from map, since modifications change hashCode
                 */
                 selectedCategory.setParent(null);
                 selectedCategory = null;
                 }
                
                 public void mapCategory() {
                 if (selectedSystemCategory == null || selectedExternalCategory == null) {
                 facesMessages.addFromResourceBundle("selectTowNodesFirst");
                 return;
                 }
                 selectedExternalCategory.getMapping().add(selectedSystemCategory);
                 }
                
                 public String getNodeFace(OfferCategory category) {
                 if (category == null) {
                 return "";
                 }
                 if (category instanceof SystemOfferCategory) {
                 return category.equals(selectedSystemCategory) ? "selected" : "unselected";
                 } else {
                 if (category.equals(selectedExternalCategory)) {
                 return "selected";
                 } else {
                 ExternalOfferCategory externalCategory = (ExternalOfferCategory) category;
                 return externalCategory.getMapping().isEmpty() ? "notMapped" : "mapped";
                 }
                
                 }
                 }
                
                 private void expandNode(UITree tree, TreeNode node) {
                 List<Object> path = new ArrayList<Object>();
                 while (node != null) {
                 path.add(((MyTreeNodeImpl) node).getId());
                 node = node.getParent();
                 }
                 Collections.reverse(path);
                 path.remove(0); // dont expand rootnode, it is not displayed
                
                 TreeState state = (TreeState) tree.getComponentState();
                
                 for (int i = 0; i < path.size(); i++) {
                 List<Object> list = new ArrayList<Object>();
                 for (int n = 0; n <= i; n++) {
                 list.add(path.get(n));
                 }
                
                 ListRowKey key = new ListRowKey(list);
                 try {
                 state.expandNode(tree, key);
                 } catch (Exception e) {
                 log.error(e.getMessage(), e);
                 }
                 }
                // ListRowKey key = new ListRowKey(path);
                // state.setSelected(key);
                 }
                
                 public Boolean adviseNodeSelected(UITree tree) {
                 return tree.getTreeNode().getData().equals(selectedCategory);
                 }
                
                 private Object generateNodeId() {
                 return childrenIdSequence++;
                 }
                
                 private UITree getSelectedTree() {
                 if(selectedCategory == null) {
                 return null;
                 }
                 return selectedCategory instanceof SystemOfferCategory ? systemTree : externalTree;
                 }
                
                 public static class MyTreeNodeImpl extends TreeNodeImpl {
                
                 private Object id;
                
                 public Object getId() {
                 return id;
                 }
                
                 public void setId(Object id) {
                 this.id = id;
                 }
                
                 public MyTreeNodeImpl() {
                 }
                
                 public MyTreeNodeImpl(Object id) {
                 setId(id);
                 }
                
                 public void addChild(MyTreeNodeImpl node) {
                 addChild(node.getId(), node);
                 }
                 }
                }
                


                • 5. Re: Two rich:trees in one form
                  Bernard Labno Master

                  I have tested that it is possible to put 2 trees in one form and everything should work fine. My other question as posted here :

                  http://jboss.com/index.html?module=bb&op=viewtopic&t=136957

                  What is expected behaviour of tree when i click twice the same node ?
                  Should processSelection be fired twice, or tree makes assumption that if nothing else was clicked then there is no need to fire it again ?