Show Comma Separated String as Detailstamp in Table

In this blog I’ll show how to implement a very specific use case which was asked on the Developer Community (the renamed OTN).

Use case

A table has a comma-separated list of values which are FK for another data store which might be from a different table or WebService. So the list of value can’t directly be related to the master table as an association or view link.

Here is a sample look of such a table or view

The resulting table in the UI should show this data as table and in the detailStamp facet show the data for each number in the list like

Implementation prerequisites

The use case is implemented using JDeveloper 11.1.1.7.0 and uses the HR DB schema.

To make it easy we use the normal HR DB and create a view to show the department together with a comma-separated list of all employeeId, EmpList in the above table. For this we use the SQL code:

CREATE OR REPLACE FORCE VIEW "HR"."DEPT_EMPS" ("DEPT_ID", "DEPARTMENT_NAME", "MANAGER_ID", "LOCATION_ID", "EMP_LIST") AS 
 SELECT DEPT.DEPARTMENT_ID,
 DEPT.DEPARTMENT_NAME,
 DEPT.MANAGER_ID,
 DEPT.LOCATION_ID,
 cclist.EMP_LIST
 FROM DEPARTMENTS DEPT,
 
 ( SELECT EMP_LIST.DEPARTMENT_ID,
 LTRIM (
 MAX (SYS_CONNECT_BY_PATH (EMPLOYEE_ID, ', '))
 KEEP (DENSE_RANK LAST ORDER BY curr),
 ', ')
 AS EMP_LIST
 FROM (SELECT ccr.DEPARTMENT_ID,
 ccs.EMPLOYEE_ID EMPLOYEE_ID,
 ROW_NUMBER ()
 OVER (PARTITION BY ccr.DEPARTMENT_ID
 ORDER BY ccs.EMPLOYEE_ID)
 AS curr,
 ROW_NUMBER ()
 OVER (PARTITION BY ccr.DEPARTMENT_ID
 ORDER BY ccs.EMPLOYEE_ID)
 - 1
 AS prev
 FROM EMPLOYEES ccs, DEPARTMENTS ccr
 WHERE ccs.DEPARTMENT_ID = ccr.DEPARTMENT_ID) EMP_LIST
 GROUP BY EMP_LIST.DEPARTMENT_ID
 CONNECT BY prev = PRIOR curr
 AND EMP_LIST.DEPARTMENT_ID = PRIOR EMP_LIST.DEPARTMENT_ID
 START WITH curr = 1
 ORDER BY EMP_LIST.DEPARTMENT_ID) cclist
 WHERE DEPT.DEPARTMENT_ID = CCLIST.DEPARTMENT_ID;

And the data store for the employees is created as a DB view too:

 CREATE OR REPLACE FORCE VIEW "HR"."EMP_LOOKUP_VW" ("EMPLOYEE_ID", "NAME", "EMP_ID_CH") AS 
 SELECT emp.EMPLOYEE_ID,
 emp.FIRST_NAME || emp.LAST_NAME,
 TO_CHAR(emp.EMPLOYEE_ID)
 
 FROM EMPLOYEES emp;

There is no association or viewlink between the two views. This data model came together with the original test case I used to implement the use case.

Implementation

The implementation starts with generating a normal ADF Web Application. Details on how to do this you find at Why and how to write reproducible test cases.

In the model layer we add EO/VO for the two views we generated to end with the following data model

And the model project

How to implement the use case

Before we start to make changes, let us think about the solution. The problem is that we have a string with a list of values which we want to show as single elements in the detail stamp of a table. We need to split the string into pieces (the employeeId) and iterate over them in the detail stamp of the table.

To iterator over a data collection, we can use a CollectionModel or an ArrayList which JDev converts internally into a CollectionModel.

In this solution, we are adding the array if employee Ids to the DeptEmpsEO as a transient attribute. This will make the array available for each row as part of the row itself.

Step 1

We add a transient attribute to the DeptEmpsEO like

null

We set the type as Object as JDev 11.1.1.9 can handle SQL Array types only. In our case, we build an ArrayList from the string.

Next, we create the Java implementation class for the EO

In the generated class we exchange the getEmpArray() method with

 

The method gets the comma-separated list and splits it into an employeeId and adds them into an ArrayList of Numbers (oracle.jbo.domain.Number). The resulting arrayList is returned.

As we have added a new attribute to the EO we have to add this attribute to the VO too:

Step 2

As we later only have employee IDs, we need a method to look up more data for the employee. Remember, the data might be from different sources so there is no association or viewlink available we can use like in normal ADFbc applications. We have to build a lookup view for this (or is the data is from a WebService we use this WebService).

We use the second DB view build at the beginning from which we created the EO EmpLookupVw and the VO EmpLookupVwView.

The data this VO presents is very simple as it’s just the employeeId, the first name, and the last name.

In this VO we create a view criteria to lookup a single employee by its Id like

Which uses a bind variable of Integer type

To make this available in the DataControl, we add the VO as special Vo to the ApplicationModule

 

 

 

Now the VO named EmpLookupById always add the viewCriteria when executed. All we have to do is to add the integer parameter for the employeeID we are looking for. In the DataControl we can use the ‘ExecuteWithParam’ method from the VO and set the parameter

Step 3: ViewController

The test case comes with some pages in the ViewController project. However, we use a fresh jspx page to show the solution. We create a new page by dragging a ‘View’ component onto the adfc-config.xml (the main unbounded task flow) and create the file by double-clicking it. In the dialog, we select a ‘Quick Layout’

 

Once the page shows up in the design view we add an af:outputText for the caption and drag the DeptEmpsVO1 view object from the data control onto the center facet of the layout and select to create an ‘ADF Read-only Table’. We show all attributes (if you like you can deselect the EmpArray attribute).

This is the base of the work. Now we enable the ‘Detail Stamp’ facet of the table. In this facet we want to iterate over the comma-separated list of employees.

We add an af:panelGroupLayout into the facet with vertical layout. Inside the panelGroupLayout we add an af:iterator which we use to iterate over the array of employeeId we created in the row as attribute EmpArray. ADF is intelligent and converts the array into a collection. This collection can be used as other collection. We define a name for the variable we can use for each single element of the collection like ‘emp’. Using an af:outputText we can print out the value of the current element of the collection like

<af:iterator id="i1" value="#{row.EmpArray}" var="emp">
                    <af:panelGroupLayout id="pgl2" layout="horizontal">
                      <af:outputText value="- #{emp} -”/>
…

What’s missing is that we like to get more information than just the Id of the employee. So we add another EL to the af:outputText which points to a bean method which will retrieve this additional information. The final facet looks like

  <f:facet name="detailStamp">
 <af:panelGroupLayout id="pgl1" layout="vertical">
 <af:iterator id="i1" value="#{row.EmpArray}" var="emp">
 <af:panelGroupLayout id="pgl2" layout="horizontal">
 <af:outputText value="- #{emp} - #{viewScope.EmpBean.empNameById}" id="ot8"/>
 </af:panelGroupLayout>
 </af:iterator>
 </af:panelGroupLayout>
 </f:facet>

Or as an image:

Finally, we have to create the bean with the method to retrieve the additional data. We create a Java class in the view folder

 

 

 

End add this bean class as managed bean to the adfc-config.xml

Before we go into writing the method to retrieve the data, we have to add the EmpLooupById.ExecuteWithParams method to the pageDef to make it available in the page. The easiest way to do this is to open the EmpLookupById Vo in the Data Control and open the ‘Operations’ node. Select the ExecuteWithParams method and drag it onto the page. We can drop it anywhere as ‘ADF Button’

 

 

 

In the final dialog, we add the value of the parameter as ‘#{emp}’ which is the EL to access the current employeeId when we iterate the array.

This will create the needed pageDef entries. As we don’t need the button, we switch to source mode and delete the button from the page. This will keep the pageDef entries.

Now we open the bean class and write the method to get the additional data for an employee as

Lines 25-38 we get the method binding from the pageDef.

Line 39 retrieves the current value of the employeeId by evaluating the EL ‘#{emp}’ by calling

Lines 40-42 set the value (the employeeId) as parameter to the methods bindId parameter

Lines 43-48 execute the method and check for any errors. If there is an error, the stack trace is printed into the log and the method return “Not found!”.

Lines 50-56 if the call the ExecuteWithParams method returns without an error, the current row of the EmpLookupById VO points to the employee we are looking for. We get the iterator from the pagedef and from the currentRow we read the ‘Name’ attribute. The value of this attribute we return and it will be printed in the af:outputText in the page.

Running the page now will produce

You can download the test case from GitHub BlogCommaSeparatedListDetail. It uses the HR DB schema with some additional DB views created. The scripts are part of the workspace. The application is developed using JDev 11.1.1.7.0.

Advertisements