The APEX Tree component is based on
jsTree which is a jQuery Tree Plugin. However not all functionality is implemented in APEX. Knowing the component on which it is based, we can find the documentation and enhance the Tree in APEX. One of these enhancements is to have checkboxes in the APEX Tree component. In this blogpost I will show you how to change the default folders to checkboxes. I will assume that you already have an APEX Tree on one of your pages, and that this one needs to be ammended to have checkboxes instead of folders.
The first thing that you want to do to make the APEX Tree easier to locate in the javascript code is to add a static ID to the Tree Region.
Navigate to the Tree Region and entry a name as the static ID, I choose "catalog-tree".
Next is to add some javascript code to the page which will modify the tree folders.
At the highest level on your page, right click and choose "Edit". Add the following code to the section labelled "Execute when Page Loads"
regTree = apex.jQuery("#catalog-tree").find("div.tree");
regTree.tree({
ui : {
theme_name : "checkbox"
},
callback : {
onchange : function(NODE, TREE_OBJ) {
if (TREE_OBJ.settings.ui.theme_name == "checkbox") {
var $this = $(NODE).is("li") ? $(NODE) : $(NODE).parent();
if ($this.children("a.unchecked").size() == 0) {
TREE_OBJ.container.find("a").addClass("unchecked");
}
$this.children("a").removeClass("clicked");
if ($this.children("a").hasClass("checked")) {
$this.find("li").andSelf().children("a").removeClass("checked").removeClass("undetermined").addClass("unchecked");
var state = 0;
} else {
$this.find("li").andSelf().children("a").removeClass("unchecked").removeClass("undetermined").addClass("checked");
var state = 1;
}
$this.parents("li").each(function() {
if (state == 1) {
if ($(this).find("a.unchecked, a.undetermined").size() - 1 > 0) {
$(this).parents("li").andSelf().children("a").removeClass("unchecked").removeClass("checked").addClass("undetermined");
return false;
} else
$(this).children("a").removeClass("unchecked").removeClass("undetermined").addClass("checked");
} else {
if ($(this).find("a.checked, a.undetermined").size() - 1 > 0) {
$(this).parents("li").andSelf().children("a").removeClass("unchecked").removeClass("checked").addClass("undetermined");
return false;
} else
$(this).children("a").removeClass("checked").removeClass("undetermined").addClass("unchecked");
}
});
}
}
,onopen : function(NODE, TREE_OBJ) {
$(NODE).removeClass("open").addClass("closed");
}
,onclose : function(NODE, TREE_OBJ) {
$(NODE).removeClass("closed").addClass("open");
}
}
});
Line 1: This will find the Tree object that we want to manipulate. Note that the jQuery selector that is used references the static ID that we assigned in the earlier step.
Lines 2 through 5:
This will change the folder-icons to checkboxes... yes, it's that easy.
Lines 6 through 37:
When you check a checkbox at a high level, all the lower levels will also be checked. When you check a node at a lower level, the level above will either get a checkmark or a square depending whether all lower levels are checked or not.
In order to equip the checkboxes with this functionality all these lines of javascript are necessary,... yes, it's that hard. :)
Lines 38 through 43:
The last two events "onopen" and "onclose" might seem strange, and they are. It seems that the little triangles used to open and close the nodes fire the opposite event. Thanks to
Christian Rokitta for discovering this oddity and providing a solution for it.
When you include the "Contract All" and "Expand All" buttons on your page, you will notice that they won't work anymore.
Change the "Expand All" button URL target to
javascript:$("#catalog-tree ul li:not(.leaf)").addClass('open').removeClass('closed');
and change the "Contract All" button URL target to
javascript:$("#catalog-tree ul li:not(.leaf)").addClass('closed').removeClass('open');
To see a working example, check out my
demo
In the next blogpost I will show you how to startup the page with the pre-selected values from the database.
Alex,
ReplyDeleteI just came accross this post regarding checkboxes in trees. I was able to incorporate it into my application without any problems. Now I would like to have the checking or unchecking of a node update a row in the database. Can you suggest how I might be able to implement this?
Any help would be appreciated.
Tom
Hi Tom,
DeleteI havent't forgotten about this question. I started writing a blogpost on it - just haven't found the time to finish it. it's coming... soon...
Alex
Thanks Alex I'm looking forward to it!
DeleteTom
Alex,
ReplyDeleteThanks for this post. Exactly what I was looking for.
--Jeff
Excellent Alex. The tree menu may be changed in this way?: When i press a parent menu, automatically open the submenus, without pressing the left triangle??
ReplyDeleteYou mean differently than the "Collapse All" and "Expand All"?
DeleteAlex, thank you very much for this post!
ReplyDeleteWould you please help me with my question? Is there any way to use jstree search in APEX tree?
Like this: regtree.jstree("search","target")?
.. as APEX tree is based on jstree, it seems possible to acess some methods
Helene
Hi Helene,
Deletehmm, that sounds like a nice challenge to figure out.... From what I see in the demo's with the search functionality it mainly works on the tree after it has been loaded. I mean the data needs to be in the tree before the search filter is applied.
If there is a lot of data to retrieve this means that the loading time would take a considerable amount of time, especially if the filter would only show 2 or 3 results.
Maybe it is a better idea to filter first before retrieving the data from the database.
Still it is an interesting challenge which I will add to my "things to figure out" - my problem currently is time - or lack thereof. I still need to finish the third blogpost on APEX Trees...
Alex
It's very nice to get your reply so soon!
ReplyDeleteThank you for advice!
I wish to highlight searched nodes without re-selecting and page submit.
However. I found the solution here:
http://tpetrus.blogspot.ru/search?q=jstree+search
I just could not get a tree reference:
$.tree.reference('tree3138412706208499').search("1");
I'm not good at jquery :(
And I'm still looking forward your future posts!
Helene
Thank you!!!! This is awesome!
ReplyDeleteThank you !! very helpful.
ReplyDeleteBut how to start an Action with selected tree lines ?
Regards
Frank
In two follow up blogposts I describe how you can save the checked values from the tree to the database and how you can use the values from the database to check the appropriate boxes in the tree.
ReplyDeleteYou can find these posts here:
Save to the database
Check the boxes with database values
This comment has been removed by the author.
DeleteHello,
DeleteI'm not very familiar with jquery.
how do I get it that only the parents can be activated?
thanks & regards
Chris
Just to clarify: do you want to be able to select only the nodes which are considered parents, i.e. not select the lowest level in the tree? Or do you want to be able to select only the root element, e.g. only select KING in the EMP-hierarchy?
DeleteHi Alex,
Deletethanks for your quick answer !
I want to be able to select each check box itself.
I want to decide which checkbox I select.
regards
Chris
Change the code "Execute when Page Loads" to the following:
DeleteregTree = apex.jQuery("#catalog-tree").find("div.tree");
regTree.tree({
ui : {
theme_name : "checkbox"
},
callback : {
onchange : function(NODE, TREE_OBJ) {
if (TREE_OBJ.settings.ui.theme_name == "checkbox") {
var $this = $(NODE).is("li") ? $(NODE) : $(NODE).parent();
$this.children("a:first-child").toggleClass("checked");
} }
,onopen : function(NODE, TREE_OBJ) {
$(NODE).removeClass("open").addClass("closed");
}
,onclose : function(NODE, TREE_OBJ) {
$(NODE).removeClass("closed").addClass("open");
}
}
});
I thank you very much Alex, it works fine.
DeleteHi Alex, I Used the above script and It's working, Thank you.
DeleteBut here, when we select the child, it doesn't show any mark on parent checkbox. Is it possible to implement that in above code?
Once, when we select some child, it should have some indication on parent so that it will be easy to know that some checkbox is selected in the Hierarchy without expanding.
Regards,
BOSE
@BOSE,
DeleteThe code in the blogpost it selfs does exactly that.. the code in response to "Anonymous" doesn't, because that's what he asked for.
Alex, I did work with both scripts and I understood its working. But Whether is it possible for code in "COMMENT" to mark(Highlight, not select) it's parent while retaining its functionality of selecting only desired checkbox.?
DeleteRegards,
BOSE
If you use the original code it will mark the parent-box, with the class "undetermined". If you want to process only the checked boxes, just don't select the ones with the "undetermined" class.
DeleteBut possibly I'm misunderstanding you completely?
Alex, My question is, Now when I use the code in "Comment" section, It will select the boxes only I want. That is perfect.
DeleteBut suppose when new user uses same the tree with preselect data, there is no marker on parent to tell new user that, there is a child leaf marked in the hierarchy. so is it possible to mark or highlight the parent when there is child selected in hierarchy.
Regards,
BOSE
Ah, I understand what you mean, but the functionality just doesn't make sense.. It might be better to explain to your users how a tree works (like the original code)
Deletethanks "Alex Nuijten" very helpful
ReplyDeletethanks Alex ,its a very good post for beginners ,I just wanted one more thing ,is it possible somehow just to keep leaf node enable for check uncheck and disable all other nodes ?
ReplyDeleteDear,
ReplyDeleteThank you. But I have a problem, my page is RTL for example (Arabic language) and every things are correct also tree is RTL but when I added the checkboxes to tree. the pointer and the line vertical are left side of node text.How can I put the right side of the node?
Regards,
Saeed
Saeed,
DeleteA little bit of googling and I stumbled across a default RTL theme created by Taha (https://groups.google.com/forum/#!topic/jstree/fQTJvLHhBq8) You might want to check out the examples that are in the demo.zip file.
Dear,
DeleteThis sample is for jstree ver 1.0 so its different with jstree 0.9.9a2 in apex 4.2.2.
Good catch! In that case you need to include ver 1.0 in your application order for it to work.
DeleteI don't know how I can use jstree 1.0 instead of tree item in apex.Could you help me about that(with sample).
DeleteRegards.
Dear Alex,
DeleteI found some posts about that,But I don't have clue about how I can use checkboxes and RTL in jstree.
I would be grateful if you make a sample about it.
https://github.com/tompetrus/oracle-apex-ajax-tree
http://tpetrus.blogspot.nl/2013_09_01_archive.html
Regards,
Saeed
I understand this can be a challenging, however my time is limited. Currently I'm very busy and can hardly find time to play with this. Of course you can hire me and I will figure out how to get this to work :)
DeleteWhen time permits again, I will figure out how it works and will blog about it (I'll put it on my "to-do-blogs" list.
Great blog, but I have one problem that you may be able to suggest a solution.
ReplyDeleteI have updated the EXPAND_ALL button, URL Taget
to
javascript:$("#catalog-tree ul li:not(.leaf)").addClass('open').removeClass('closed');
The browser updates the page and quickly goes to page that has the URL of javascript:$("#catalog-tree ul li:not(.leaf)").addClass('open').removeClass('closed');
and displays
[object Object]
as the browser content.
Any suggestions to prevent this behaviour?
What is the static ID of the Tree Region? In my case it is "catalog-tree". The code in the EXPAND_ALL button locates the html-element with the id "catalog-tree" (#catalog-tree).
DeleteAlex - thanks for your response.
ReplyDeleteI have solved by problem by pasting your string into the 'Button Attributes' as
onclick="$('#catalog-tree ul li:not(.leaf)').addClass('closed').removeClass('open');"
Note that I had to ensure the ' and " quotations are consistent.
And set the 'Action when Button Clicked' to the
'Action Defined by Dynamic Action'
I debugged this error by using Firefox/Firebug to inspect my Apex page and your demo page ( http://apex.oracle.com/pls/apex/f?p=47888:13:15989733398094:::::).
So having a working example added significant assistance.
Thaks again
Hey Alex,
ReplyDeleteThanks a lot...
Can u help me in integrating these check-boxes with check-boxes I am having in flat view of hierarchy.
I am having a radio button to display data both in tree and report format and now I want to have all those check-box selected in report which are selected in my hierarchical view.
From your description I can't deduce what you want to implement on your page. Why don't you create an example on apex.oracle.com so I can look at that?
DeleteI am having check-box with tree view and leaves of it are represented in a report with check-boxes (APEX_ITEM.CHECKBOX). Now I want to integrate both of them. like from moving from one view to another same rows remain selected.
DeleteWhen the data is stored in the table, it shouldn't be too hard to show a "flat report" with the appropriate checkboxes checked? When you don't want to store the data, then you would have to resort to javascript to handle this... but then you would have to consider pagination of the report and the sort order... that might get tricky.
DeleteStoring the data (there is another blogpost on how to do that: http://nuijten.blogspot.nl/2013/11/tree-with-checkboxes-save-data-js-array.html) is the easiest solution.
Thanks, will try to work this out.
DeleteHey Alex,
ReplyDeleteThanks a lot...
Can u help me in integrating these check-boxes with check-boxes I am having in flat view of hierarchy.
I am having a radio button to display data both in tree and report format and now I want to have all those check-box selected in report which are selected in my hierarchical view.
Hi Alex,
ReplyDeleteWhat should be the change if we want check-boxes in leaves only.
see this comment
DeleteHi Alex,
ReplyDeleteGreat post.
I have a flat report based on a view that has some columns coming from its ancestor tables.
Item Id, Item Name, Item Attr 1, Item Attr2, Item Parent Id, Item Parent Name, Item Grand Parent Id, Item Grand Parent Name, Item Great Grand Parent Id, Item Great Grand Parent Name, Item Great Great Grand Parent id, Item great Great Grand Parent Name
_______________________________________________________________________________________________________________________________________
...
Users need to be able to filter on the name of the ancestor columns.
Instead of having 4 multiple select list boxes (one for each parent name, grand parent name, great grand parent name, great great grand parent name), I would rather use a checked box tree.
- Great Great Grand Parent 1
| |
| |__ - Great Grand Parent 1.1
| | |
| | |__ - Grand Parent 1.1.1
| | | |
| | | |__ - Parent 1.1.1.1
| | | |
| | | |__ - Parent 1.1.1.2
| | |
| | |__ - Grand Parent 1.1.2
| | | |__ - Parent 1.1.2.1
| | | |
| | | |__ - Parent 1.1.2.2
| | | |
| | | |__ - Parent 1.1.2.3
| | |
| | |__ + Grand Parent 1.1.3
...
When user press the 'Retrieve Data' button the screen need to dynamic build the SQL based on the list of checked tree node.
In fact only selecting the checked leaf nodes (which are at parent level) and concatenating their ids should be enough.
WHERE item.parent_id IN ( :PXX_CHECKED_PARENT_IDS )
Any idea how in javascript I can select only the leaf nodes of the tree and concatenate their id into an ORACLE APEX item.
Hello Alex, Its very informative blog regarding checkboxes with APEX tree.
ReplyDeleteI have used your above code to generate the same, but In my APEX page checkboxes are appearing but "Arrows" are missing.
Please suggest the solution. I am using the ORACLE APEX vesrion 4.2.1
The "arrows" are the ones that open and close the details, I assume. Obvious question: are there details, i.e. is there a hierarchy in the query; do you have different levels?
DeleteYes, The "arrows" are the ones that open and close the details. I also have different levels in hierarchy.
DeleteStrange,... are you using a custom template?
DeleteNote that changing the tree won't work in APEX5 unless you keep using the legacy tree, instead of the new APEX tree.
Alex,It was templated based issue. Solved it. Thank you for the help.
DeleteHi Alex,
ReplyDeleteThis is an excellent article!
I followed the steps you mentioned above and was able to create a tree with checkboxes without difficulty.
I even got the Expand and Collapse buttons to work.
There is only one thing that didnt work for me - the tooltip.
Although I have specified the database column name as tooltip in the tree query it doesnt show up when I mouse over a tree node.
Would appreciate your inputs on this.
Thank a lot!
Regards,
Priya
Hi Priya,
DeleteThe tooltip should work, the code just extends the tree-functionality. With APEX 5 the tree has been rewritten (jsTree is still there, but will probably de deprecated). This code doesn't work with that new APEX-tree.
Hi Alex, this is great, but I seem to be having issues with a small bug that I hope you can fix. When selecting/deselecting siblings in the last branch of my tree, the correct checked/undetermined classes don't seem to be applied correctly to the parents. as seen here: https://www.dropbox.com/s/m4g7vp6wrxy269h/Capture.PNG?dl=0
ReplyDeleteAre you able to offer any clues?
Thanks!
This comment has been removed by the author.
ReplyDeleteHi Alex, how I can change the icon (or theme) for the root node?. It is posible to change other nodes or disable or enable? Thanks. =)
ReplyDeleteHi Salvador,
DeleteYou probably can, best refer to the documentation of jsTree
Hi Alex,
ReplyDeleteI use the Jslegacy Tree. I want the parent node check box to be hidden. ie. the parent has child level, it should only have the triangle to expand the parent and i do not want a checkbox.
Please help me to achieve this.
Hi Alex,
ReplyDeleteI use the Jslegacy Tree. I want the parent node check box to be hidden. ie. the parent has child level, it should only have the triangle to expand the parent and i do not want a checkbox.
Please help me to achieve this.
Hi Alex,
ReplyDeletePlease ignore my previous post, i have achieved the same. thank you.