Oracle APEX: Customizing row rendering in Tree Regions using renderNodeContent


Introduction
When working with a tree region, developers have limited options with how to change how a Tree region is rendered. Out of the box, developers can specify the node label, node link and the node icon. These slots, in addition to custom CSS, can achieve a desired result the majority of the time. However, what if you have a need to display more information in different slots in a way that simply using CSS to customize the look would not be sufficient?
For this blog, imagine that the requirement is to display an employee tree based on the EMP table, but also displaying each employee’s salary in a color coordinated pill based on the salary bracket. This will be achieved by overriding the treeNodeAdapter’s renderNodeContent function.
Passing the data to the JavaScript function
Before defining the rendering, we need to define how we will pass the data from the database to the rendering function. The renderNodeContent receives pNode as an argument, so we are limited to how we can package the data. Generally, the best approach is to pack the data into a string datatype, either as character separated string or as a JSON and set that column value into the Node Value Column. This string will be then mapped to pNode.id in renderNodeContent.
Since in this example the only extra data needed is the salary, we will simply set the Node Value Column to SAL.
select empno
, ename
, mgr
, sal
from emp
renderNodeContent
To override the renderNodeContent function, we must override the Tree region’s treeNodeAdapter with a custom one where we override the renderNodeContent function in the region’s Initialization JavaScript Function.
function(options){
options.makeNodeAdapter = function(pStaticData, types, pHasIdentity) {
let adapter = $.apex.treeView.makeDefaultNodeAdapter(pStaticData, {}, pHasIdentity);
adapter.renderNodeContent = function(pNode, pOut, pOptions, pState) {
// TO BE DEFINED BELOW
};
return adapter;
};
return options;
}
Rendering Boilerplate
Since we are completely overwriting the function, if we want to have the default tree rendering as a starting point use this boilerplate.
adapter.renderNodeContent = function(pNode, pOut, pOptions, pState) {
let icon = adapter.getIcon(pNode);
if(!icon) {
icon = types["default"].icon; // gets the default icon if not provided to this node
}
pOut.markup('<span class="fa ' + icon + '" aria-hidden="true"></span>'); // icon span
pOut.markup('<span tabindex="-1" role="treeitem" class="a-TreeView-label" aria-level="1" aria-selected="false">' + pNode.label + '</span>'); // label span classes
};
As you can see, using this boilerplate recreates the default rendering of the Tree region.
Finalized Example
Using the boilerplate as a starting point, we can add our custom logic to the renderNodeContent function to add a pill displaying the employee’s salary with a color coordinated to the salary.
adapter.renderNodeContent = function(pNode, pOut, pOptions, pState) {
let icon = adapter.getIcon(pNode);
if(!icon) {
icon = types["default"].icon;
}
let label = pNode.label;
let salary = pNode.id;
let color, bg_color;
// decide what colors to use
if(salary > 3000) {
bg_color = "#B0D8A4";
color = "#698162";
}
else if (salary > 1500) {
bg_color = "#FEE191";
color = "#988757";
}
else {
bg_color = "#FF4F69";
color = "#8B2734";
}
// Boiler plate //
pOut.markup('<span class="fa ' + icon + '" aria-hidden="true"></span>');
pOut.markup('<span tabindex="-1" role="treeitem" class="a-TreeView-label" aria-level="1" aria-selected="false">' + label + '</span>');
// Custom HTML to add salary pill
pOut.markup('<div style="margin-left: auto;">'); // div to push pill to the right
pOut.markup('<span style="background-color:' + bg_color + '; color:' + color + '; padding:5px; border-radius:5px; margin-left:5px; border-width: 1px; border-color: black; border-style: solid;">$' + salary + '/week</span>');
pOut.markup('</div>');
};
Here is the an example of the completed tree (Note: I increased the value of --a-treeview-node-line-height in the region to add more spacing between nodes) :
Subscribe to my newsletter
Read articles from Jack Drolet directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
