Quantcast
Channel: Save Your Knowledge
Viewing all articles
Browse latest Browse all 9

Collapse other nodes on rich:tree node selection

$
0
0

A requirement on software i developing is concerned rich:tree selection.
My customer wants that the node expansion must be performed not only on “plus/minus icon” but also on simple node selection. Than on node selection must be expanded ONLY the path to that node and all others must be collapsed.

I realized that using changeExpandListener and nodeSelectListener events. The first one is triggered on clicking on “plus/minus icon” of the tree. The other one on node selection.

The idea is to tracking, on backing-bean, the current and the old TreeRowKeys selected and, on changeExpandListener or nodeSelectListner events, collapse old TreeRowKey and expand current one.

Let’s see xhtml implementation:

<rich:tree switchType="ajax" binding="${gestioneArchivioMenuBean.tree}" id="gestioneArchivioTree" ajaxSingle="true">
	<!-- ROOT NODE -->
	<rich:treeNodesAdaptor id="adaptorRoot" nodes="${backingBean.treeModelList}" var="root">
		<rich:treeNode changeExpandListener="${backingBean.processExpansion}"
                               nodeSelectListener="${backingBean.selectNode}" 
                               data="${root.desc}">
		  <f:facet name="icon">
			<h:graphicImage value="/img/sitemap.png"/>
		  </f:facet>
		</rich:treeNode>
		   <!-- FIRST LEVEL NODES -->
		   <rich:treeNodesAdaptor id="adaptorFirstLevel" nodes="${root.subDir}" var="firstLevel">
			   <rich:treeNode  changeExpandListener="${backingBean.processExpansion}"
                                           nodeSelectListener="${backingBean.selectNode}" 
                                           data="${firstLevel.desc}">
				   <f:facet name="icon">
					  <h:graphicImage value="${firstLevel.imageUrl}"/>
				   </f:facet>
			   </rich:treeNode>             
					 <!-- SECOND LEVEL NODES -->
					<rich:treeNodesAdaptor id="adaptorSecondLevel" 
                                                               nodes="${firstLevel.subDir}" 
                                                                var="secondLevel">
						<rich:treeNode  changeExpandListener="${backingBean.processExpansion}"       
                                                                nodeSelectListener="${backingBean.selectNode}" 
                                                                data="${secondLevel.desc}">
						   <f:facet name="icon">
							 <h:graphicImage value="${secondLevel.imageUrl}"/>
						  </f:facet>  	
						</rich:treeNode>
					</rich:treeNodesAdaptor>
		</rich:treeNodesAdaptor>
	</rich:treeNodesAdaptor>
</rich:tree>

As you can see i set on each rich:treeNode element, the two events described before, bound to two actionListeners on backingBean. So every time i expand/collapse or select a node the two methods are triggered.

Here is the interesting portion of my backingBean:

public class treeManager {

import org.richfaces.component.UITree;
import org.richfaces.component.html.HtmlTreeNode;
import org.richfaces.event.NodeExpandedEvent;
import org.richfaces.event.NodeSelectedEvent;
import org.richfaces.model.TreeRowKey;
import javax.faces.component.UIComponent;

//this attribute stores old selection tree node
private TreeRowKey oldSelectionTree;

//The method is called on node select event
public void selectNode(NodeSelectedEvent ev) throws IOException {

		//The event source is an HtmlTreeNode object, from which i take UITree ref
		UITree tree = ((HtmlTreeNode) ev.getSource()).getUITree();
		
	    //tracking of oldSelection and currentSelection
	    
	    // oldSelectionTree is a class attribute
		TreeRowKey oldSel = oldSelectionTree;
		//new selection is get from tree object
		TreeRowKey newSel = (TreeRowKey) tree.getRowKey();

		/*i check the exists new and old selection and that the currentSelection's depth is 
		  less than new one or equals.*/
		if (newSel != null && oldSel != null && newSel.depth() <= oldSel.depth()) {
			
				//in this case iterating on oldSel, i collapse nodes until i arrive 
				//to same depth of newSel
				while (null != oldSel && oldSel.depth() > 1
						&& oldSel.depth() >= newSel.depth()) {
					tree.queueNodeCollapse(oldSel);
					oldSel = oldSel.getParentKey();
				}
				
		//if newSel's depth is greater than oldSel's one i have to do nothing because
		//surely i'm expanding a child of oldSel and for this reason it haven't to be collapsed


        //i expand the new node (if this event is triggered from processExpansion method
        //i'm just re-doing the expanding), if no i wants that on node selection, it must be
        //expanded
		tree.queueNodeExpand(newSel);
		
		//i set oldSelectionTree as newSel
		this.oldSelectionTree = newSel;

	}

//This method is triggered on "expand/collapse" icon on the tree.
	public void processExpansion(NodeExpandedEvent evt) throws IOException {

		Object source = evt.getSource();
		
		if (source instanceof HtmlTreeNode) {
			HtmlTreeNode node = (HtmlTreeNode) source;
			//i take tree ref
			UITree tree = ((HtmlTreeNode) source).getUITree();
			
			//and current selection (the node relative to "expand/collapse" icon clicked
			TreeRowKey newSel = (TreeRowKey) tree.getRowKey();
			
			//i create a NodeSelectedEvent setting HtmlTreeNode as source and oldSelectionTree
			//as oldSelected attribut of the event. It's not very important because
			//i get old selected node from oldSelectedTree class attribute in selectNode event
			NodeSelectedEvent ev = new NodeSelectedEvent((UIComponent) node,
					oldSelectionTree);
					
			//and i call selectNode event method, in this way all is managed from that method,
			//to make two behaviours synchronized
			selectNode(ev);
			
			oldSelectionTree = newSel;

		}
	}
}

As you can see in selectNode method i track old and new node selections and, if newSel’s depth is less or equals to oldSel’s one, i simply collapse iteratively oldSel until it’s depth is equals to newSel’s one. At the end of methods i call a queueNodeExpand so, also on selecting node ,it will expand itself.

processExpansion method manage on Expand/Collapse event. To have the two events synchronized, in this method, i only build a NodeSelectEvent to call selectNode and let it to do everything.-



Viewing all articles
Browse latest Browse all 9

Trending Articles