3 Replies Latest reply: May 3, 2013 6:27 AM by Alexander Scheffler RSS

Creating a dynamic context menu dependent on the <rich:tree> selection

Karsten Wutzke Expert

Hello,

 

I have a tree on a page to which I'd like to attach a dynamic context menu depending on the name of the node and its type:

<h:form id="tree-form">
  <rich:panel header="Sort Tree">
    <rich:tree value="#{nodeManager.rootTreeNode}"
               var="treeNode"
               nodeType="#{treeNode.type}"
               toggleType="client"
               selectionType="ajax"
               selectionChangeListener="#{nodeManager.selectionChanged}"
               id="document-tree">
        
      <rich:treeNode type="root" id="root-node">
        ...
      </rich:treeNode>
  
      <rich:treeNode type="chapter" id="chapter-node">
        ...
      </rich:treeNode>
      
      ...
      
    </rich:tree>
      
    <rich:contextMenu target="document-tree" mode="ajax">
      <rich:menuItem label="Add chapter to #{nodeManager.selectedNode.name}..." />
      <rich:menuItem label="Remove #{nodeManager.selectedNode.name}..."
                     rendered="#{nodeManager.selectedNodeClassName eq 'ChapterNode'}" />
    </rich:contextMenu>

  </rich:panel>
</h:form>

 

NodeManager is a view-scoped bean, which successfully receives the selection changes when the node is left- or right-clicked. It also overwrites the selected node in that bean from which getSelectedNode() returns the instance and getSelectedNodeClassName() returns the simple class name, here "RootNode" or "ChapterNode".

 

What I want now is a dynamic context menu: roots cannot be deleted (no REMOVE shown), new chapters can only be added to roots or chapters (ADD always shown).

 

RootNode shall show menu:

 

* Add chapter to [node name]...

 

ChapterNode shall show menu:

 

* Add chapter to [node name]...

* Remove [node name]...

 

Here's the relevant NodeManager code:

public void selectionChanged(TreeSelectionChangeEvent event)
{
    log.info("Tree selection!");
        
    List<Object> selection = new ArrayList<Object>(event.getNewSelection());
    Object currentSelectionKey = selection.get(0);
    
    UITree tree = (UITree)event.getSource();
    Object storedKey = tree.getRowKey();
    tree.setRowKey(currentSelectionKey);
    selectedContainerTreeNode = (ContainerTreeNode)tree.getRowData();
    tree.setRowKey(storedKey);
    
    log.info("Selected node = " + selectedContainerTreeNode.getName());
}

public Node getSelectedNode()
{
    return selectedContainerTreeNode != null ? selectedContainerTreeNode.getNode() : null;
}

public String getSelectedNodeClassName()
{
    String className = selectedContainerTreeNode != null ? selectedContainerTreeNode.getNode().getClass().getSimpleName() : null;
    
    log.info("Selected node class name = " + className);
    
    return className;
}

 

However, the dynamically set selected node and its class name returned are always null, as #{nodeManager.selectedNode.name} renders nothing (empty string?) and the rendered="#{nodeManager.selectedNodeClassName eq 'ChapterNode'}" *always* seems to evaluate to false, even though the selectionChanged listener clearly prints to the console that a new tree node has successfully been set:

 

12:27:20,662 INFO  [com.company.project.facade.NodeManager] (http-localhost-127.0.0.1-8080-3) Selected node class name = null
12:27:26,082 INFO  [com.company.project.facade.NodeManager] (http-localhost-127.0.0.1-8080-3) Selected node class name = null
12:27:26,083 INFO  [com.company.project.facade.NodeManager] (http-localhost-127.0.0.1-8080-3) Tree selection!
12:27:26,083 INFO  [com.company.project.facade.NodeManager] (http-localhost-127.0.0.1-8080-3) Selected node = wwwwwwwwww
12:27:26,145 INFO  [com.company.project.facade.NodeManager] (http-localhost-127.0.0.1-8080-3) Selected node class name = RootNode
12:27:26,177 INFO  [com.company.project.facade.NodeManager] (http-localhost-127.0.0.1-8080-3) Selected node class name = RootNode
12:27:26,307 INFO  [com.company.project.facade.NodeManager] (http-localhost-127.0.0.1-8080-3) Selected node class name = RootNode
12:27:27,368 INFO  [com.company.project.facade.NodeManager] (http-localhost-127.0.0.1-8080-3) Selected node class name = RootNode
12:27:27,369 INFO  [com.company.project.facade.NodeManager] (http-localhost-127.0.0.1-8080-3) Tree selection!
12:27:27,369 INFO  [com.company.project.facade.NodeManager] (http-localhost-127.0.0.1-8080-3) Selected node = subb
12:27:27,427 INFO  [com.company.project.facade.NodeManager] (http-localhost-127.0.0.1-8080-3) Selected node class name = ChapterNode
12:27:27,463 INFO  [com.company.project.facade.NodeManager] (http-localhost-127.0.0.1-8080-3) Selected node class name = ChapterNode
12:27:27,581 INFO  [com.company.project.facade.NodeManager] (http-localhost-127.0.0.1-8080-3) Selected node class name = ChapterNode

 

What's wrong with the above approach? How do you fix this?

 

Karsten

  • 1. Re: Creating a dynamic context menu dependent on the <rich:tree> selection
    garagoth Newbie

    This is because your contextMenu is rendering at the same time when tree is rendering for the forst time on your page.

    Unless you re-render context menu, it will show initial content every time.

     

    I am also struggling with this, and so far I have no solution when to re-render context menu. Simply adding rerender="contextMenuId" to a tree causes menu to appear for a moment and then disappear.

     

    M.

  • 2. Re: Creating a dynamic context menu dependent on the <rich:tree> selection
    Greg C Newbie

    Have you tried wrapping the context menu in an outputPanel?

    I suspect..(haven't tried the code that was given).

    That the context menu may get rendered initially...but then is lost from the page.

    <rich:contextMenu target="document-tree" mode="ajax">
          <rich:menuItem label="Add chapter to #{nodeManager.selectedNode.name}..." />       <rich:menuItem label="Remove #{nodeManager.selectedNode.name}..."                     rendered="#{nodeManager.selectedNodeClassName eq 'ChapterNode'}" />    </rich:contextMenu>
    

     

     

    try with

     

    <a4j:outputPanel id="myContextMenuPanel_Id" > 
      <rich:contextMenu target="document-tree" mode="ajax">      <rich:menuItem label="Add chapter to #{nodeManager.selectedNode.name}..." />      <rich:menuItem label="Remove #{nodeManager.selectedNode.name}..."                     rendered="#{nodeManager.selectedNodeClassName eq 'ChapterNode'}" />    </rich:contextMenu>
    </a4j:outputPanel>
    

     

     

     

    But don't forget to add the panel as a target for rendering on the tree....

     

    Wrapping them is how I do mine..and I haven't had a problem with it...

     

    - Sorry about the formatting...I'm not great with the wiki markup.

  • 3. Re: Creating a dynamic context menu dependent on the <rich:tree> selection
    Alexander Scheffler Newbie

    I have the same problem. We solved it with richfaces 3.3 but now we want to migrate to richfaces 4.3.1.Final and I am not able to wangle this again...

    With Richfaces 3.3 our tree looks like the following:

     

    {noformat}

    <rich:tree

        switchType="ajax"

        nodeSelectListener="#{someBean.processNodeSelect}"

        adviseNodeOpened="#{someBean.isAdviseNodeOpened}"

        value="#{someBean.treeNode}"

        var="item"

        treeNodeVar="nodeItem"

        reRender="..."

        ajaxSubmitSelection="true"

        id="treeView"

        >

        <rich:treeNode

            id="TreeElement">

     

            <h:outputText value="#{item.displayName}" />

     

            <a4j:support

                event="oncontextmenu"

                disableDefault="true"

                oncomplete="#{rich:component('contextmenu')}.doShow(event, {})"

                reRender="documentContextMenu"

                status="globalStatus">

                <a4j:actionparam

                    value="#{item.id}"

                    name="treeNodeItem"

                    assignTo="#{someBean.menuSelectedNodeId}"

                    actionListener="#{someBean.processCreateContextMenu}" />

            </a4j:support>

        </rich:treeNode>

    </rich:tree>

    {noformat}

     

    and the contextmenu looks like

     

    {noformat}

    <a4j:outputPanel

        layout="inline"

        id="documentContextMenu">

         <rich:contextMenu

             disableDefaultMenu="true"

             attached="false"

             id="contextmenu"

             binding="#{someBean.contextMenu}"

             submitMode="ajax">

            <a4j:support

                event="onclick"

                reRender="..."

                status="globalStatus">

            </a4j:support>

         </rich:contextMenu>

    </a4j:outputPanel>

    {noformat}

     

    I wonder, is there any chance to rebuild this in richfaces 4? Now or in the future?

     

     

    Thanks

    Alex