Monday, November 23, 2009

Tree Table Component in Oracle ADF

I want to share today, how we are using Tree Table components in Oracle ADF. Sure, you can implement Tree or Tree Table just by reading Oracle JDeveloper 11g documentation. However, I will try aggregate different information and to put all pieces together, in order to help those developers who are just starting to use Tree components and are curious how things work. My today post is based on information from Frank Nimphius article available on ADF Code Corner - How-to access the selected row data in a TreeTable or Tree.

Download sample application developed in JDeveloper 11g R1 PS1 - TreeComponents.zip. In this sample I'm using two Tree Table components, second is dependent on first. Dependency could easily work through View Link Master-Detail relationship, but I decided to make use case a bit more complex and to filter second Tree Table from Selection Listener method. Also, I decided to include editable fields into both Tree Table components.

First Tree Table represents three level Master-Detail relationship between Regions, Countries and Locations. Second Tree Table is Master -Detail between Departments and Employees:


Its worth to mention, that Departments VO contains Bind Variable and Where clause, this allows me to filter second Tree Table from first Tree Table Selection Listener method:


On the layout side in ViewController, I have used Panel Dashboard (new component in JDeveloper 11g) - it allows to align your panels with Tree Table components in very easy and elegant way:


When you decide to create Tree or Tree Table, you should drag and drop from Data Control only Master VO. From a wizard you can add Tree Levels (VO details) and specify attributes to be shown on each level:


When Tree Table will be generated, if you want to have multiple columns, you should add Column components manually and define output/input components manually. Make sure you are assigning correct values from node binding (see my sample application):


Second Tree Table is refreshed based on selection changes in first Tree Table, this means we should declare Partial Trigger dependency:


We override default Selection Listener on first Tree Table in order to perform filtering on second Tree Table component:


In Selection Listener I'm using Java code from Frank Nimphius article mentioned above. I'm accessing currently selected row and if this row belongs to third level (Locations), I'm filtering second Tree Table:


On runtime you can see how nicely Panel Dashboard allocates two Tree Table components in Panel Boxes:


Expanded Tree Table view, with editable columns:

46 comments:

  1. Tree table component will not use the detail instances added in the data model
    rather it uses the view link accessor driven rowsets to show up children.
    So you can remove all the detail instances on a view object which tree model is based on as they get executed unncessarly when the currency is set on master.

    ReplyDelete
  2. Hello,

    I have tried this before, if I will remove them, I will not be able to declare through #{node} binding and tree rendering will fail.

    Regards,
    Andrejus

    ReplyDelete
  3. Hi there,

    What about re-rendering the tree after a commit. I've just started with ADF and when I insert a new element to the tree if it is a root node the tree does not render to show the included node (it works fine for children nodes though).

    Any hints?

    Regards,

    Luppi

    ReplyDelete
  4. hi mr. Andrejus
    very efficient task , but you did not mention " executeWithParams" action and how you add it , or
    can you recommend docs to read about it

    ReplyDelete
  5. Hi,

    ExecuteWithParams is standard ADF operation, you can check in ADF Developer Guide.

    Andrejus

    ReplyDelete
  6. Hi Andrejus,
    Do you have any idea about implimenting the same in oracle forms.Can i make this as a jar and use it in Oracle forms.

    ReplyDelete
  7. Hi,

    I don't think you can use it in Forms.

    Regards,
    Andrejus

    ReplyDelete
  8. Hi to all. I have not any chance to move my application to 11g and I'm needing to show some search results in a treeTable 10g component, but I have to show more than one root node per page. ¿Does somebody know if there is a way to do that in 10g?. I really appreciate any help: code sample, blog post or recommendations. Thanks in advance. Mauricio.

    ReplyDelete
  9. Hi Andrejus,

    Thank you for this solution. I implemented it to my application, and it was working fine (I thought) with 11.1.1.3. Now we upgraded to 11.1.1.4 and I encountered an issue. (I am not sure if it is related to the upgrade or not. It could be me causing this problem.)
    The issue is that it does not delete grand children nodes. The tree structure displays correctly in my tree table. When I select a parent node and then try to delete it and its all descendants, it just deletes the direct child and the parent node itself. When I debug, getChildren() returns null for the child node though the child node has several grand children nodes. The funny thing is that when I select the child node and try to delete it, getChildren() returns the correct number of elements.
    Do you have any suggestions for me? Thank you for your help in advance!

    ReplyDelete
  10. Hi,

    Latest public release is 11.1.1.3. I cant test with internal 11.1.1.4

    Regards,
    Andrejus

    ReplyDelete
  11. Hello Andrejus,
    What if you do not have an entity object? I am trying to do something similar but I have a view object and view link getting the information from the database table. When I create the tree and the detail table, nothing gets populated. Any suggestions as to what I might be doing wrong?
    Thanks
    -H

    ReplyDelete
  12. Hi,

    It should work even without EO, it should be fine to have only VO and ViewLink.

    Most probably there is problem in VO.

    Regards,
    Andrejus

    ReplyDelete
  13. Well the adf Tree works. But the detail table does not work. It defaults to the first value. And does not even change when I click on something on the tree. (I do have partial Trigger).
    Weird thing is my AppModule works fine so I know the logic with the view links works okay.
    Any idea what else it could be?
    Thanks
    -H

    ReplyDelete
  14. Hey Andrejus,

    I'm to refine the classical master-detail app using tables into "tree as master tree/table as detail" application.
    The problem is the binding for the detail,(as each part uses its own model, tree and table respectively)
    so detail shows all data despite the master record clicked in the tree.

    Could you comment on to help please?

    Regards,
    Alexander Bondarenko
    oradevpro@yandex.ru
    ----------------------
    here's one more, with my e-mail,
    so please disregard the previous
    post

    ReplyDelete
  15. Is there a way to show an attribute of a table that is a view accessor? Like when building LOV's except in a tree table? All I want is to show a Name instead of the id. I did it in a form using view accessors and made it an LOV that shows the Name instead of the ID. I wanna do the same thing for a tree table except read only (not LOV) and name instead of id. I wanna do this without creating a view link if at all possible (I did it for the form without using a view link) I am using ADF and Jdeveloper

    ReplyDelete
  16. You can simply add Name field as new tree attribute - it will be rendered then. You need to have View Link, for Master-Detail relationship implementation.

    Andrejus

    ReplyDelete
  17. I agree but I can have an LOV that lets me choose the names instead of IDs but cannot have a tree table that instead of an ID shows the names? That is what made me think it could be possible but you know this stuff a billion times better than me, sorry if the question was stupid

    ReplyDelete
  18. Is there a way to have a select one choice LOV in a query?

    ReplyDelete
  19. Sorry, I dont understand the question...

    ReplyDelete
  20. Is there a way when making a view criteria and dropping it on the page to make somehow some of the fields select one choice LOV's?
    Also while I am asking, is there any way to make manual columns on the second detail level of a tree table?

    THANKS

    ReplyDelete
  21. I am trying to implement tree table, but I have a view object and view link getting the information from the database table. When I create the tree and the detail table, nothing gets populated. Any suggestions as to what I might be doing wrong?
    Thanks
    -Mamatha

    ReplyDelete
  22. Hi Andrejus,

    I am working on ADF tree and have come across a issue. I am populating my tree with ADFBC. The tree is of two level. The root level is the rows of the view object MenuTableView and the second level values are populated from SubMenuVO. The views have parent child relationship.

    I want to manipulate the rows before they are rendered as trees. By manipulation I mean I might delete some of the rows based on user permission before they are rendered as a tree. What is the best way to do this?

    How does a adf tree calls the AM & VO classes? I just added this code in my AM but I dont get any sysout? Seems the AM methods are not called by the tree except the constructor.

    public class MenuAMImpl extends ApplicationModuleImpl {
    /**
    * This is the default constructor (do not remove).
    */
    public MenuAMImpl() {
    System.out.println("MenuAMImpl instantiated");
    }

    public static void main(String arg[]){
    new MenuAMImpl().getMenuTableView1();
    }

    /**
    * Container's getter for MenuTableView1.
    * @return MenuTableView1
    */
    public MenuTableViewImpl getMenuTableView1() {
    System.out.println("--- Inside getMenuTableView method of AM ---");
    ApplicationModule am=Configuration.createRootApplicationModule("model.am.MenuAM", "MenuAM");
    MenuTableViewImpl menuView=(MenuTableViewImpl)am.findViewObject("MenuTableView1");

    //RowSetIterator iter=menuView.getRowSet();
    while(menuView.hasNext()){
    MenuTableViewRowImpl mainRow= (MenuTableViewRowImpl)menuView.next();
    System.out.println("-- AM - parent row-"+mainRow.getPermissionExp());
    System.out.println("-- AM - parent row-"+mainRow.getMenuDisplayName());
    SubMenuVOImpl submenuView=(SubMenuVOImpl)mainRow.getSubMenuVO();
    while(submenuView.hasNext()){
    SubMenuVORowImpl childRow=(SubMenuVORowImpl)submenuView.next();
    System.out.println("-- AM - child row-"+childRow.getPermissionExp());
    System.out.println("-- AM - child row-"+childRow.getMenuDisplayName());
    }
    }
    return menuView;
    }

    /**
    * Container's getter for SubMenuVO1.
    * @return SubMenuVO1
    */
    public SubMenuVOImpl getSubMenuVO1() {
    return (SubMenuVOImpl)findViewObject("SubMenuVO1");
    }

    /**
    * Container's getter for ParentChild1.
    * @return ParentChild1
    */
    public ViewLinkImpl getParentChild1() {
    return (ViewLinkImpl)findViewLink("ParentChild1");
    }


    }

    The idea is to eliminate the rows at either parent or child here istelf , I mean the AM. but seems the VO's are getting called by some other method and not getter methods of AM? How the the VO's getting called in case of tree?

    ReplyDelete
  23. Hi,

    Probably you would need to override VO Impl class, and work with rowset. Not AM Impl class.

    Andrejus

    ReplyDelete
  24. Hello Andrejus, thanks for the post.
    I would like to know if you could show us how to use the treetable together with a separate table, to represent a Master-Detail-Detail three-level relationship. In this case I am facing the same problem as Harleen, that is: "The detail table does not work. It defaults to the first value. And does not even change when I click on something on the tree. (I do have partial Trigger).
    Weird thing is my AppModule works fine so I know the logic with the view links works okay."
    Is there a way to fix the behaviour on the UI side by using bindings properly?

    ReplyDelete
  25. Hi,

    I need to create tree from this kind of data.

    col1 col2 col3 col4
    ---------------------------------
    A1 B1 C1 D1
    A1 B1 C1 D2
    A1 B1 C2 D3


    I want to display the tree as :

    A1
    ---->B1
    --------->C1
    -------------->D1
    -------------->D2
    --------->C2
    -------------->D3

    I dont have parentid, child_id kind of relationship in the table, so cannot create a self accessor view link.
    How can I do that? Please help.

    Thanks,
    Sachin

    ReplyDelete
  26. Hi,
    Search on Table Tree View is not working as expected.

    I'm working on a Table Tree View, and am facing some strange problem.

    Lets say the Tree view is as follows. FetchSIze is 1000.

    Case#1 Search Box: XA -> ENTER

    A
    XA
    XB
    XC
    B

    C

    D


    E

    It works fine, it starts search from A and goes fine.

    Case#2: Search Box: XA -> ENTER

    If Search for same thing again, it starts search from B or C, not from A.

    Code for the same:


    CollectionModel model = (CollectionModel) tree1.getValue();
    treeBinding = (JUCtrlHierBinding) model.getWrappedData();
    JUCtrlHierNodeBinding root = treeBinding.getRootNodeBinding();

    Found = false;
    RowKeySet resultRowKeySet =
    searchTreeNode(root, searchType, searchString);
    RowKeySet disclosedRowKeySet =
    buildDisclosedRowKeySet(treeBinding, resultRowKeySet);


    tree1.setSelectedRowKeys(resultRowKeySet);
    tree1.setDisclosedRowKeys(disclosedRowKeySet);

    AdfFacesContext.getCurrentInstance().addPartialTarget(tree1);

    private RowKeySet searchTreeNode(JUCtrlHierNodeBinding node,
    String searchType,
    String searchString) {

    RowKeySetImpl rowKeys = new RowKeySetImpl();

    if (Found == true) {
    return rowKeys;
    }

    Row nodeRow = node.getRow();
    if (nodeRow != null) {
    String compareString = "";

    Object attribute = nodeRow.getAttribute("ATTRIBUTENAME");
    if (attribute instanceof String) {
    compareString = (String)attribute;
    } else {
    compareString = attribute.toString();
    }

    if (compareString.equals(searchString) {

    rowKeys.add(node.getKeyPath());
    Found = true;
    }
    }

    If (FOUND = false) {
    List children = node.getChildren();

    if (children != null ) {

    for(JUCtrlHierNodeBinding _node: children) {
    RowKeySet rks = searchTreeNode(_node, searchType, searchString);
    if (rks != null && rks.size() > 0) {
    rowKeys.addAll(rks);
    }

    }

    }
    }


    return rowKeys;

    }

    It seems like Children cursor is set to an index, may be I need to reset it

    Please suggest.

    ReplyDelete
  27. In continuation to the above comment: Search on Tree Table is not worknig as expected.

    The problem is "The child nodes iterator get set to last search node or something". The below method is setting that it seems:
    private RowKeySet buildDisclosedRowKeySet(JUCtrlHierBinding treeBinding,
    RowKeySet keys) {

    RowKeySetImpl discloseRowKeyset = new RowKeySetImpl();

    Iterator iter = keys.iterator();

    while(iter.hasNext()) {


    List keyPath = (List)iter.next();
    JUCtrlHierNodeBinding node =
    treeBinding.findNodeByKeyPath(keyPath);

    if (node != null && node.getParent() != null &&
    !node.getParent().getKeyPath().isEmpty()) {

    discloseRowKeyset.add(node.getParent().getKeyPath());

    RowKeySetImpl parentKeySet = new RowKeySetImpl();
    parentKeySet.add(node.getParent().getKeyPath());

    RowKeySet rks = buildDisclosedRowKeySet(treeBinding, parentKeySet);
    discloseRowKeyset.addAll(rks);
    }


    }

    return discloseRowKeyset;

    }


    Can you please suggest how to resolve this?

    ReplyDelete
  28. In continuation to above Search on Tree Table not working as expected....

    Solution:

    After changing the RangeSize on ViewObject from 25 to 100, it worked.

    As there are more than 25 nodes under the root, it picked up the next 25 from current selection.

    ReplyDelete
  29. Your approach is wrong - it will be performance killer.

    You should search directly inside table, construct node key and disclose it. Dont search through ADF Tree directly - this is my advice.

    Andrejus

    ReplyDelete
  30. Hi Andrejus,
    I am trying to improve the performance of the tree table and facing few issues in doing this...
    I am using tree table to represent Question - Answer list at different levels and the view link is self referenced. We are showing only one column of the tree table with the following details...

    Question Desc
    Answer - this can be text, checkbox, radio - User has the option to enter the answer and save it.
    User has the provision to enter comments and bugs along with the answer. For this two links are provided Add Bug and Add Comment, when user clicks on Add Comment link is hidden and one text area is shown to the user.
    If there are any existing comments they will be shown below.

    Comments and Bugs are separate VO's and they have view link with the QuestionAnswerVO. In the bean class, I wrote the logic to get the RowIterator of the CommentsVO and BugsVO for each question and send it as list to UI.

    Here are the two performance issues that I am facing...
    1. When I click on Add Comment sometimes it is taking long time to show the text area to enter the comment. When I click on Add Comment I have PPR to refresh only Add Comment link and text area component.
    I observed that when I click on Add Comment link it is submitting the whole page and the complete tree table is loaded again and it is taking lot of time for this to complete.

    Could you please suggest any idea to improve the performance?

    Thanks,
    Ravindra

    ReplyDelete
  31. In addition to my previous comment on performance issue...

    Based on the answer selected for the parent question I have to refresh the child questions
    Assume that the hierarchy is like this...
    Q1 (if answer of the parent question is set to Yes, then I have to show only the questions which are set to Yes. In this case if the parent answer is Yes then I have to show only Q2. If it is No I have to show Q3 and Q4)

    - Q2 (Yes)
    - Q3 (No)
    - Q4 (No)

    For this I am handling the logic as below...
    1.If the values are already stored in the database then doing the below logic...
    In VOImpl.java
    @Override
    protected ViewRowSetImpl createViewLinkAccessorRS(AssociationDefImpl associationDefImpl,
    ViewObjectImpl viewObjectImpl,
    Row row,
    Object[] object) {
    ViewRowSetImpl viewRowSetImpl = super.createViewLinkAccessorRS(associationDefImpl, viewObjectImpl,

    if (columnName.equalsIgnoreCase("AnswerVO")) {
    //We will execute the below logic only if the user has entered an answer
    if (isBooleanQts && (answer != null && answer.trim().length() > 0)) {
    ViewCriteriaManager vcm = viewObjectImpl.getViewCriteriaManager();
    ViewCriteria vc = vcm.getViewCriteria("EnableChildQuestions");
    VariableValueManager vvm = vc.ensureVariableManager();
    if ("Yes".equalsIgnoreCase(answer)) {
    vvm.setVariableValue("answerBind", "Yes");
    } else {
    vvm.setVariableValue("answerBind", "No");
    }
    viewObjectImpl.applyViewCriteria(vc);
    } else {
    viewObjectImpl.removeApplyViewCriteriaName("AnswerChecklistEnableOnParentVOCriteria");
    }
    }
    }
    2. If the values are dynamically by the user then in the value change event I wrote the below logic...
    Row currentSelectedRow = found[0];//Get the current selected row
    currentSelectedRow.setAttribute("Answer", newValue);
    RowSet childAnswerChecklistRowSet = (RowSet)currentSelectedRow.getAttribute("AnswerChecklistVO_2");
    if (childAnswerChecklistRowSet != null) {
    childAnswerChecklistRowSet.executeQuery();
    }

    if (rowDataObj.hasChildren()) {
    DCIteratorBinding childIterBinding = rowDataObj.getChildIteratorBinding();
    RowSetIterator rowSetIter = childIterBinding.getRowSetIterator();
    Row[] rowsArray = rowSetIter.getFilteredRows("EnableOnParentAnswer", newValue.equals("Yes") ?"No" : "Yes");
    for (Row row : rowsArray) {
    row.removeFromCollection();
    }
    }

    Could you please suggest any idea to improve the performance?

    Thanks,
    Ravindra

    ReplyDelete
  32. Hi,

    You can try to see if this hint will help you:

    http://andrejusb.blogspot.com/2011/12/tuning-adf-tree-retain-view-link.html

    Andrejus

    ReplyDelete
  33. Andrejus,
    If I set that flag I could not able to see Comments and Bugs i.e. the VO iterator's that I fetch for each row.

    Thanks,
    Ravindra

    ReplyDelete
  34. Hi,

    I see this is complex case, I can't give advice my comment. Proper advice would require work on your code...

    Andrejus

    ReplyDelete
  35. Andrejus,
    Please suggest is anything that I can do for this?

    1.
    In each row of the tree table I have few links that show additional data. When I click on these links the size of the row is getting expanded, I mean the height of the row gets increased and this results in last row getting shrinked. I am seeing this issue only when I try to refresh particular component in a row and not the whole tree table.
    Code: My tree table is having following structure...


    inside center facet.

    2. In the popup if I scroll the tree table till the last row and close the popup and open again it is not showing from the first row it opens the poup which will show the last few rows and user has to use the scroll bar to move up.

    Thanks,
    Ravindra

    ReplyDelete
  36. Hi Andrejus,

    I have created two programmatic VOs with parent child relationship.There are no entity objects as my data is coming from web-service.

    I am able to display master detail relationship using ADF tables but when I am using ADF tree, only parent elements are populated.

    Do we need some extra work for Trees for programmatic VO?

    Thanks,
    Minal

    ReplyDelete
  37. Hello, i'm trying to add a child node with a button, the idea is that when i click Add in the current node, i need to add a child on the tree table, i've tried catching the current node and creating a row with the parent values, and it works but it adds to the tree table 2 rows, one at the level required and one more at the principal parent node, any suggest?

    ReplyDelete
  38. hi , i have a scenario where i am using check boxes to select detail part of tree
    and on a button click i have to get all checked rows
    how can i do this?

    ReplyDelete
  39. Our app has a large af:tree component. It was bound to backing bean in pageflowscope. Realizing that's it's not good to bind components to anything longer than requestScope I changed the binding to use ComponentReference as specified by best practices. This broke our desired behavior for when we navigate from tree to page that edits' the node, then back to the tree. The disclosed state is not being remembered anymore. How do I restore the tree state so that all disclosed rows remain disclosed when navigating away and then back?

    ReplyDelete
  40. Your able is still based on ADF BC? May be try to use Retain View Link Accessor option for VO tuning.

    Andrejus

    ReplyDelete
  41. Is it possible to get the Master table in editable mode. I have a requirement where I need to show Department(Master) -> employee(Details) in tree table but I want Department attributes to be editable. Is this possible using af:treeTable?

    ReplyDelete
  42. Hi Andrejus,

    I am trying to form a tree table based on read only view object where query return data as below

    Employee-ID-1 FirstName1 LastName1 Document1
    Employee-ID-1 FirstName1 LastName1 Document2
    Employee-ID-2 FirstName2 LastName2 Document3
    Employee-ID-2 FirstName2 LastName2 Document4
    Employee-ID-3 FirstName3 LastName3 Document5
    Employee-ID-3 FirstName3 LastName3 Document6

    In tree table we would like to display as below

    Employee-ID-1 FirstName1 LastName1
    Document1
    Document2
    Employee-ID-2 FirstName2 LastName2
    Document3
    Document4
    Employee-ID-3 FirstName3 LastName3
    Document5
    Document6


    Question: Can we achieve this tree table from Read Only View Object?

    Thanks in advance.

    ReplyDelete
  43. Tree table using ADF Read Only View Objects

    ReplyDelete
  44. Thanks for the article.
    The sample code is not available: http://jdevsamples.googlecode.com/files/TreeComponents.zip yields an error 404
    Is there a place where I can get this code?
    Thanks in advance

    ReplyDelete
  45. I have changed download URL, try now - should be working.

    Andrejus

    ReplyDelete
  46. Hello, the new versions of jdeveloper (12.1.3), variable #{node} doesn't show the attributes of table child. How I can show the values of table child into the af:columns like your example with new versions of jdeveloper?

    Thanks an regards.

    ReplyDelete