Add Checkbox Selection to APEX Tree Region

Hopefully you have already converted to the APEX Tree implementation. This is important because the deprecated jsTree implementation will be removed in the next release of APEX so that we can update jQuery and Oracle JET libraries to the latest versions. Some people are still using the jsTree implementation because they have custom code to do things such as checkbox selection. Questions about how to do this and other things with the APEX Tree have come up many times on the APEX forums.

This article will describe how to add multiple selection with checkboxes to the APEX tree region. You can try it out here. You can also download the app. It is a 5.1.1 app but this technique should work in 5.0 as well.

The APEX Tree region is intentionally very simple. It is just for display of a hierarchy with simple single selection or navigation. The treeView widget can do much more but the “documentation” is only in the source file. Also it can be difficult to change some settings because you don’t have direct access to how the widget is created. (Note: The new 5.1 Tree attribute Advanced: Initialization JavaScript Code doesn’t actually do anything. It should not have been added like that but hopefully is an indication of things to come.)

It is a fair amount of code to get this working and before I show that I should make it clear that you don’t actually need a checkbox to do multiple selection. The treeView widget has an option to enable multiple selection. The user can then use the normal Shift and Ctrl key modifiers with the mouse or keyboard to select zero or more nodes. To enable multiple selection, simply add to a Page Load Dynamic Action or Page attribute JavaScript: Execute when Page Loads:

$("#mytree").treeView("option","multiple", "true")

Replace the selector mytree with your tree id. At anytime you can get the selected elements or nodes with:

$("#mytree").treeView("getSelection")
$("#mytree").treeView("getSelectedNodes")

The getSelectedNodes method returns an array of node objects. Each node can have these properties.

  • id – This comes from the value SQL column.
  • icon – The optional icon.
  • label – This is the label text of the node that is shown. It comes from the title SQL column.
  • link – The optional link URL.
  • tooltip – This is the optional tooltip.

Most of the time it is the id that you will be interested in. The selected ids can be sent to the server for processing. The treeView widget allows any number of properties in a node but this is all that is supported by the APEX Tree region SQL.

Getting back to checkboxes. Some people prefer a checkbox for selecting so lets do that. The approach used here is similar to Interactive Grid. Rather than use a checkbox type input element a font icon that looks like a checkbox is used. The icon should show the selection state and this can be done simply with CSS using the existing is-selected class; one icon for checked and one for unchecked. Clicking on the icon should change the selection state of just that node. This does not affect normal selection using mouse and keyboard.

The treeView has a separate view and data model layer with an adapter interface between them. The adapter interface has a renderNodeContent method that gives complete control over how the node DOM content is rendered. By overriding that method a span for the checkbox font icon can be added. A delegated click handler will also be needed.

To try this out create a page with a tree region. Then add the following to the page attribute JavaScript: Execute when Page Loads:

var tree$ = $("#mytree"), // change this to use whatever id you give the tree
    nodeAdapter = tree$.treeView("getNodeAdapter");

// Set a custom node renderer that adds a checkbox icon span
nodeAdapter.renderNodeContent = function( node, out, options, state ) {
    out.markup("<span class='treeSelCheck'></span>"); // this is the checkbox - its not a real checkbox input
    // the rest of this code is essentially a copy of what is in widget.treeView.js function renderTreeNodeContent
    if ( this.getIcon ) {
        icon = this.getIcon( node );
        if ( icon !== null ) {
            out.markup( "<span" ).attr( "class", options.iconType + " " + icon ).markup( "></span>" );
        }
    }
    link = options.useLinks && this.getLink && this.getLink( node );
    if ( link ) {
        elementName = "a";
    } else {
        elementName = "span";
    }
    out.markup( "<" + elementName + " tabIndex='-1' role='treeitem'" ).attr( "class",options.labelClass )
        .optionalAttr( "href", link )
        .attr( "aria-level", state.level )
        .attr( "aria-selected", state.selected ? "true" : "false" )
        .optionalAttr( "aria-disabled", state.disabled ? "true" : null )
        .optionalAttr( "aria-expanded", state.hasChildren === false ? null : state.expanded ? "true" : "false" )
        .markup( ">" )
        .content( this.getLabel( node ) )
        .markup( "</" + elementName + ">" );
};

tree$.treeView("option","multiple", true) // make the tree support multiple selection
    .treeView("refresh") // refresh so that all the nodes are redrawn with custom renderer
    .on("click", ".treeSelCheck", function(event) { // make that "checkbox" span control the selection
        var nodeContent$ = $(event.target).closest(".a-TreeView-content"),
            isSelected = nodeContent$.hasClass("is-selected"),
            selection$ = tree$.treeView("getSelection");
        if ( isSelected ) {
            selection$ = selection$.not(nodeContent$);
        } else {
            selection$ = selection$.add(nodeContent$);
        }
        tree$.treeView("setSelection", selection$);
        return false; // stop propagation and prevent default
    });

The custom checkbox markup needs custom styles as well. Add the following to page attribute CSS: Inline.

.treeSelCheck {
    display: inline-block;
    font: normal normal normal 14px/1 FontAwesome;
    text-rendering: auto;
    width: 14px;
    margin-right: 4px;
    cursor: default;
}

/* fa-square-o */
.treeSelCheck::before {
    content: "?";
}

/* fa-check-square-o */
.is-selected > .treeSelCheck::before {
    content: "?";
}

Here I have used Font Awesome icons but Font APEX could be substituted. The content characters seem to not be saving correctly they are Unicode f096 for square-o and f046 for check-square-o.

The demo app also demonstrates how to submit the selected node ids and initialize the selection. It has a page item called P2_SELECTED_NODES. Typically this would be a hidden item. There is a dynamic action using the custom event treeviewselectionchange to update the item with the new selection each time it changes. The following code is added at the end of JavaScript: Execute when Page Loads.

// setSelectedNodes only works if the node has been rendered so do expand all
// this is not ideal but much simpler than having to check each node and expand parents as neeeded, remove if not needed
tree$.treeView("expandAll");
tree$.treeView("setSelectedNodes", $v("P2_SELECTED_NODES").split(":").map(function(i) { return { id: i }; }));

That is it. One less reason to use jsTree. I hope this gets easier in the future. Ideally the treeView widget would have a “useCheckbox” option exposed as a declarative attribute.

2 thoughts on “Add Checkbox Selection to APEX Tree Region

  1. Thank excellent article, it worked for me, I’m also interested to know how can i load these checkbox values from database? if possible.

    Thanks,

Leave a Reply

Your email address will not be published. Required fields are marked *