Maintaining APEX Tree Region Expansion State

Play this article

As soon as I was finished with the first incarnation of the ORDS REST Workshop for release in APEX 18.1, I had a list as long as my arm of things that I wanted to do to improve it. Some small, some large.

One of the things that bugs me the most is that the APEX Tree region on the left side of the page doesn’t, by default, remember its expansion state. Originally that meant that every time you clicked a node and navigated to a new page, it would fully collapse on itself. Thinking that I would have time to come back and fix it later, I implemented a quick fix to make the tree fully expand every time. The problem was that, while working on the other, more esoteric functionality, I let too much time slip by and I wan’t able to go back and implement a proper solution before it was time for UI freeze.

If you’re working with a relatively small number of RESTful services, this doesn’t pose too much of a problem. However, if you’re working with more than just a few, having the tree fully expended with every page refresh very quickly starts to get annoying.

So, for APEX 19.1 one of my first priorities was to fix this. Little did I know how simple the fix really was.

Documentation to the Rescue

One of the things that has improved in the more recent versions of APEX has been the JavaScript API documentation. Included therein is documentation on the treeVeiw object used by the APEX Tree Region.

A quick peruse of the methods shows that there are a a number of ways we could approach the problem but after a little reading I landed on the technique outlined below.

I’m sure there are other ways to do this and probably at least 50% of them will be better than my way, but it works and its simple. Two criteria I try to let guide my coding!

Creating the tree is the easy part. In my example I used a simple tree over the EMP table. Just for fun I hooked it up to a classic report that shows the direct reports of the person selected in the tree. Single-clicking on a tree node would navigate back to the same page, passing the EMPNO of the user you selected so that it could be used in the report.

For maintaining the Tree’s sate, the first problem to attack was that of getting and saving the trees expanded state so that it can be reinstated when you come back to the page.

For this I chose to use the getExpandedNodeIds method. This method returns an array of node ids from the associated data model for each expanded node. Because I wanted to save the tree state, regardless of what caused the navigation, I chose to create a Dynamic Action triggered on Page Unload.

tree1.webp

The True action of the Dynamic Action would be to execute the following JavaScript:

// Get a handle on the tree using regionStaticID_tree. Then use the treeView to get the expanded Nodes
var exp$ = $("#empTree_tree").treeView("getExpandedNodeIds");
// Create a cookie and store the nodes that are expanded
// The toString will change the array into a comma separated string.
apex.storage.setCookie('myTreeState', exp$.toString());
  • Line 2 gets the selected nodes and stores them in the array named exp$
  • Line 5 uses the apex.storage.setCookie javascript function to store the selected nodes in the users’s browser. the exp$.toString() function basically takes the array and creates a comma separated string that is easy to store

When navigating away from the page for any reason, the values of the selected nodes are now stored in a browser cookie called myTreeState.

Now all we need to do is restore that state when we’re going the other direction. For this we’ll use another Dynamic Action triggered On Page Load.

tree2.webp

The True action of the Dynamic Action would be to execute the following JavaScript:

// Get a handle on the tree using regionStaticID_tree
var tree$ = $("#empTree_tree");
// Get the value of the cookie named myTreeState
var myTreeState = apex.storage.getCookie("myTreeState");
// If the Cookie is not null then loop through and expand the tree
if (myTreeState) 
{
    // Split the value of the cookie and make an array
    var $expNodes = myTreeState.split(",");
    // Loop Through the array and for each entry
    $expNodes.forEach(function (id) {
        // Identify the Tree Nodes that relate to the saved values
        var node$ = tree$.treeView("find",{ 
            depth : -1, 
            findAll: false, 
            match: function(node){ 
                return node.id == id
            }
    }) 
    // Expand those nodes.
    tree$.treeView("expand", node$)
    }) 
}
  • Line 2 gets a handle on the tree itself and assigns it to tree$
  • Line 4 uses apex.storage.getCookie to retrieve any values from the user’s browser into the variable myTreeState
  • Line 6 checks to see if there were any values to retrieve. If there weren’t then there is no need to reinstate them.
  • Line 9 splits the string that was stored and creates an array of node ids called $expNodes
  • Line 11 loops through each item in the array
  • Lines 13 through 19 identify the tree nodes that match the values we saved and creates an array of node$
  • Line 21 accesses the treeView of tree$ and expands the nodes held in the array.

The end result is that the tree will be set back to its previous state after having navigated away and then back.

I hope that someone finds this useful and that it shows just how simple it can be to interact with the APEX JavaScript APIs now that there is good documentation.

Did you find this article valuable?

Support Doug Gault by becoming a sponsor. Any amount is appreciated!