Technology: ADF11g
Developed in: JDeveloper 11.1.1.3.0
Browsers tested: Firefox 6.0.1 and Internet explorer 8 (8.0.6001.18702 64 bit edition)
Summary
I received a question is it possible to create a representation like shown below in ADF? In this blog a description how to solve this.
Database
In a database a table tst_cases is created:
Column name | Datatype | Mandatory | Constraint(s) |
---|---|---|---|
ID | Number(10,0) | Yes | Primary key |
Name | Varchar2(100) | Yes | Unique key |
PARENT_CSE_ID | Number(10,0) | No | Foreign key to ID |
It's filled with content:
ID | NAME | PARENT_CSE_ID |
---|---|---|
1 | Case 1 | |
10 | Case 10 | 1 |
20 | Case 20 | 1 |
30 | Case 30 | 1 |
101 | Case 101 | 10 |
102 | Case 102 | 10 |
201 | Case 201 | 20 |
202 | Case 202 | 20 |
203 | Case 203 | 20 |
Model layer
Create with the Business Components for Tables an entity for the table (name Case), a view object for the entity (name CasesView) and an application module (name TestAppModule). An association and view link will be created as well.
Create another view object with name RootCasesView that uses the Case enity object but with where clause PARENT_CSE_ID IS NULL. Create a view link from the RootCasesView to the CasesViwe (using the Case_ParentCase_FK association):
View controller
- Create a page (HierachyViewer.jspx) and add it to the adfc-config.
- Drag and drop from the data controls palette the RootCasesView as hierarchy viewer:
- Choose radial:
- Select in hierarchy all 3 levels (and accept all other default values):
You can click on the + sign underneath the Case 10 box and then the page looks like this:
I would like to display all children on page opening. To achieve this you can just set the displayLevelsChildren property on the dvt:hierarchyViewer tag to 100 then it will display 100 children (if they exist). However I choose to set this property to the max number of levels that are currently present in the database table.
Determine the max number of levels
To determine the max number of levels 2 functions are created (implementation of the functions are found at the end of the blog):
- Get_levels_to_root (i_n_cse_id IN tst_cases.id%TYPE); this function returns the number of levels between i_n_cse_id and the root tst_cases (with parent_cse_id null)
- Get_max_number_of_children; this functions loops over all tst_cases and executes get_levels_to_root for the current id, the max number found is returned.
A managed bean (request scope) is created to execute this view object and return the MaxNumber attribute as int:
The implementation of this bean:
package nl.capgemini.marianneHorsch.view;
import javax.faces.context.FacesContext;
import nl.capgemini.marianneHorsch.model.services.TestAppModuleImpl;
import nl.capgemini.marianneHorsch.model.views.MaxNumberOfChildrenViewImpl;
import nl.capgemini.marianneHorsch.model.views.MaxNumberOfChildrenViewRowImpl;
import oracle.adf.model.binding.DCBindingContainer;
import oracle.adf.model.binding.DCDataControl;
import oracle.binding.BindingContainer;
public class TestHierarchyViewBean {
public TestHierarchyViewBean() {
super();
}
public int getMaxNumberOfChildren() {
TestAppModuleImpl service = getService();
MaxNumberOfChildrenViewImpl view = service.getMaxNumberOfChildrenView();
view.executeQuery();
MaxNumberOfChildrenViewRowImpl row =
(MaxNumberOfChildrenViewRowImpl)view.first();
return (row == null ? 1 : (row.getMaxNumber() == null ? 1 :
row.getMaxNumber().intValue()));
}
public TestAppModuleImpl getService() {
DCBindingContainer bc = (DCBindingContainer)FacesContext
.getCurrentInstance().getApplication().evaluateExpressionGet
(FacesContext.getCurrentInstance(), "#{bindings}",
BindingContainer.class);
if (bc == null) {
return null;
}
DCDataControl dc = bc.findDataControl("TestAppModuleDataControl");
if (dc == null) {
return null;
}
return (TestAppModuleImpl)dc.getDataProvider();
}
}
If we now run the page it opens like:
Function: get_levels_to_root
CREATE OR REPLACE FUNCTION get_levels_to_root
( i_n_cse_id IN tst_cases.id%TYPE)
RETURN NUMBER
AS
--
CURSOR c_cse (b_parent_id tst_cases.parent_cse_id%TYPE)
IS
SELECT id, name, parent_cse_id
FROM tst_cases
WHERE id = b_parent_id;
r_cse c_cse%ROWTYPE;
l_b_found BOOLEAN;
--
l_n_id tst_cases.id%TYPE;
l_n_levels NUMBER(10,0) := 0;
--
BEGIN
--
l_n_id := i_n_cse_id;
--
WHILE (l_n_id IS NOT NULL)
LOOP
--
OPEN c_cse (b_parent_id => l_n_id);
FETCH c_cse
INTO r_cse;
l_b_found := c_cse%FOUND;
CLOSE c_cse;
--
IF (l_b_found)
THEN
l_n_levels := l_n_levels + 1;
l_n_id := r_cse.parent_cse_id;
ELSE
l_n_id := NULL;
END IF;
--
END LOOP;
--
RETURN l_n_levels;
--
END get_levels_to_root;
Function: get_max_number_of_children
CREATE OR REPLACE FUNCTION get_max_number_of_children
RETURN NUMBER
AS
--
l_n_number NUMBER(10,0);
l_n_max_number NUMBER(10,0) := 0;
--
BEGIN
--
FOR r IN (SELECT id
FROM tst_cases)
LOOP
--
l_n_number := get_levels_to_root(i_n_cse_id =>r.id);
IF (l_n_number > l_n_max_number)
THEN
l_n_max_number := l_n_number;
END IF;
--
END LOOP;
--
RETURN l_n_max_number;
--
END get_max_number_of_children;