Recently I have had several occasions where I had to implement a List of Values, allowing the users to select a value from a large set of reference values. In several instances, this set of reference data values was quite large, and could easily be organized and presented in an hierarchical fashion: through a tree. With JHeadstart and ADF UIX, it is pretty simple to generate such a dynamic, data driven tree. It is also quite simple to generate a data entry page, either single or multi-record, with a call to a List of Values to retrieve a reference value. In this post I will show how I used a combination of these two features to create a data entry page that uses a popup List of Values data tree to select reference values from. It involves a few – but only a few – post-generation changes. Note: I have used JDeveloper 10.1.2 and the most recent 10.1.2.1 release of JHeadstart. I have provided the JDeveloper project for download; however, in order to get it to work, you need to install JHeadstart yourself.
This is what the final result looks like, in terms of EMP and DEPT:
Steps for creating the Tree List of Values
These are the steps for creating this application:
- Start JDeveloper
- Create a New Application Workspace, Default Web Technology Template
- In the Model Project, create Business Components from Tables for EMP and DEPT. If not automatically created, create ViewLinks from Emp.deptno to Dept.deptno and from Emp.mgr to Emp.empno.
- Now carefully create the Application Module’s Data Model, as shown in this picture:
We see root level ViewObject instances for EmpView, DeptView and again for DeptView, named TreeRootDept. That last one will server as the root of our LOV-tree. Under this instance, we created nested instances of EmpView, called TreeNodeEmp1, TreeNodeEmp2, TreeNodeEmp3 etc., for as many levels as our tree will contain. Note that we leverage the ViewLinks we just created when setting up this Data Model.
Now we will focus on the ViewController project:
- From the RMB on the ViewController project, select the option “Enable JHeadstart on this Project “
- Again, from the RMB on the ViewController project, select the option “New JHeadstart Application Structure File”
- Open the JHeadstart Application Structure File. Create Groups and Lookups as shown in this picture:
We see Base Groups for Emp and Dept. The latter is irrelevant by the way for this article. The Base Group for Emp contains a Lookup on Dept – choice that selects Dname and Deptno from DeptView – as well as a Lookup on Emp – lov that selects Ename and Empno from EmpView, to retrieve a manager.We also see a Base Group TreeEmployee. This is the one that causes the Department and Employee Tree to be generated. The base group has a Tree View Object Usage of TreeRootDept. The detail group has a Tree View Object Usage of TreeNodeEmp1. Note that the JHeadstart Application Generator and/or the JHeadstart and ADF runtime will pick up the underlying, nested ViewObject Usages – TreeNodeEmp2 and TreeNodeEmp3 – itself.
- Generate the application using the JHeadstart Application Generator
- Run the generated application. The result is seen below, with the “classic” List of Values popped up from the Employee page:
The generated application currently contains the Employees Tree. We will use it later on to transform into our List of Values:
Now it is time for some small post-generation changes that will allow us to select a Manager from the Tree-based List of Values:
- Make the tree nodes selectable: Open file TreeEmployeeTree.uit and replace styledText with:
<!-- <styledText rendered="${uix.current.hierType.name=='TreeNodeEmp1Node'}" text="${uix.current.Ename}"/> --> <link rendered="${uix.current.hierType.name=='TreeNodeEmp1Node'}" text="${uix.current.Ename}" destination="javascript:selectedEmployee( ${uix.current.Empno}, '${uix.current.Ename}');"/>
- Add JavaScript function selectEmployee: Open file TreeEmployee.uix and add the JavaScript fragment to the HEAD element: Also change the title in the HEAD element to something more suitable to our LOV:
<head title="Select a Manager"> <contents> <script text=" function selectedEmployee( empno, ename) { top.opener.selectEmployee(empno, ename); top.close(); }; "/> </contents> </head>
- Refine the layout of the Tree page: Still in TreeEmployee.uix, make some changes inside the BODY element:
<body> <contents> <pageLayout> <!-- <templates:standardPageLayout routerAction="TreeEmployee.do"> <tabs> <templates:HrmServiceTabBar selectedIndex="2"/> </tabs> <pageHeader> <templates:TreeEmployeeLevel2Tabs selectedIndex="0"/> </pageHeader> --> <start> <templates:TreeEmployeeTree/> </start> <!-- </templates:standardPageLayout> --> </pageLayout> </contents> </body>
The generated and subsequently post-generation modified Tree looks like this:
- Add JavaScript Function to receive the selected Employee: Open file EmpTable.uix. Add a fragment of JavaScript to the HEAD element:
<head title="${nls.TABLE_TITLE_EMP}"> <contents> <html:style type="text/css"> .invisible {display:none;} .visible {display:block;} </html:style> <script text="function selectEmployee(empno , ename) { /* original row from which the LOV was invoked: */ var originalFieldName = _LovNM[_LovFI]; /* behind the last : we find the row number */ pos = originalFieldName.lastIndexOf(':'); var rowid = _LovNM[_LovFI].substring(pos+1) ; document.dataForm['EmpView1:Mgr:'+rowid].value = empno; document.dataForm['EmpView1:ManagerName:'+rowid].value = ename; }" /> </contents> </head>
- Make sure that the Foreign Key Attribute is represented in the calling page: still in EmpTable.uix, add a hidden form element for the Mgr attribute:
... <tableFormat tableBanding="rowBanding"/> <contents> <formValue value="${uix.current.rowKeyStr}" name="rowKeyStr" id="${ui:concat('EmpView1:rowKeyStr:',uix.current.tableIndex)}"/> <formValue value="${uix.current.Mgr}" name="Mgr" id="${ui:concat('EmpView1:Mgr:',uix.current.tableIndex)}"/> <column>...
- Re-route the LOV invocation to our Tree based LOV: still in EmpTable.uix, locate the messageLovInput element for the ManagerName attribute. Currently, the destination of this item – the URL that is loaded in the popup window – is the one generated by JHeadstart. We are going to override it with a call to the TreeEmployees.do target:
<messageLovInput id="${ui:concat('EmpView1:ManagerName:',uix.current.tableIndex)}" model="${uix.current.EmpManagerName}" text="${ui:cond(param.source==ui:concat('EmpView1:ManagerName:',uix.current.tableIndex) and not empty param.searchText and 'false'!=requestScope.showLovWindow and 'lovUpdate'!=param.event,param.searchText,uix.current.EmpManagerName)}" name="ManagerName_No_Update" promptAndAccessKey="&Manager" destination="TreeEmployee.do?pageTimeStamp=ignore" partialRenderMode="multiple" partialTargets="messageBox hasChanges _uixState ${ui:concat('EmpView1:Mgr:',ui:concat(uix.current.tableIndex,ui:concat(' EmpView1:rowKeyStr:',uix.current.tableIndex)))}" showWindow="${requestScope.showLovWindow}" unvalidated="false" validateBlanks="true" onLovValidate="ValidateCallBackEmpManagerName" onLovSelect="LovSelectCallBack" columns="10" rows="1" readOnly="false" />
It is crucial here to include the pageTimeStamp=ignore request parameter – this relieves the burden of pageTimeStamp validation browser back button protection for the LovWindow, that would otherwise prevent a commit of values selected from the LOV.
- Remove the Employee Tree from the Menu: open file HrmServiceTabBar.uit and comment out or delete the link:
<!-- <link text="Employee Tree" destination="javascript:doNavigate('StartTreeEmployee.do');"/> -->
Download JDeveloper 10.1.2 Workspace with all source code: HrmTreeLovTrial.zip.
Next Steps and Comments
One thing that would probably have been better: just like the normal UIX LOV can do, this Tree LOV probably should perform an AJAX call UIX style, a primaryClientAction followed by the partial page refresh; this entails a proper update of the Model and subsequent refresh of the Client (browser). That would for example lift the current requirement that the Foreign Key ‘field’ (in this case Mgr) must be included as hidden form field in the page.
I have already created a slightly more complex example of a Tree LOV that allows the user to select multiple values from the tree, returning a list of selected nodes to the calling window. These are then used to create intersection records – or update detail records in a master-detail situation. Note that this is fairly similar to the use of Shuttles with JHeadstart.