JDev: af:panelList without bullet if no link is given

We all know that ADF components are well defined and have a lot of functions. However, what if we want to use a component but don’t like what we get out of the box from it?

The answer is easy most of the times as we can change the look of the component or its behavior to our needs. Sometimes the answer is not as straightforward, but still easy, as in this

Use Case

A user wants to have an af:panelList, showing bullets in front of each item in the list. The Items should be links to other pages. The problem part is that some of the links in the list should not be visible all the time. E.g. a user might not have to needed access right to some of the links.

Problem

When we use an af:panelList as is, we get the following look

From this setup

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE html>
<f:view xmlns:f="http://java.sun.com/jsf/core" xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
    <af:document title="panelList.jsf" id="d1">
        <af:form id="f1">
            <af:panelGridLayout id="pgl1">
                <af:gridRow height="50px" id="gr1">
                    <af:gridCell width="100%" halign="stretch" valign="stretch" id="gc1">
                        <!-- Header -->
                    </af:gridCell>
                </af:gridRow>
                <af:gridRow height="100%" id="gr2">
                    <af:gridCell width="100%" halign="stretch" valign="stretch" id="gc2">
                        <af:panelList rows="5"> 
                            <af:link text="link 1" id="l1" destination="http://www.oracle.com"/>
                            <af:link text="link 2" id="l2" destination="http://www.oracle.com"/>
                            <af:link text="link 3" id="l3" destination="http://www.oracle.com"/>
                            <af:link text="link 4" id="l4" destination="http://www.oracle.com"/>
                        </af:panelList>
                    </af:gridCell>
                </af:gridRow>
            </af:panelGridLayout>
        </af:form>
    </af:document>
</f:view>

Setting e.g. link 3 to not visible we get

So, seeing the bullet in front of the link isn’t what we are looking for. Using the rendered property instead of the visible property will give us

But the problem now is that the link can’t be simply brought back to the page without a full page refresh. That is one of the disadvantages of using the rendered property. Once a component is not rendered, you need to do a full page refresh to get it back. A partial page refresh won’t work.

If you want to show some white space for the missing ‘link 3’ you can’t use the rendered property at all. Putting a spacer between ‘link 2’ and ‘link 4’ you end up with the same image as you get for using the visible property.

Solution

One solution is to omit the bullet in front of the links, which is automatically generated by the component. If there is no bullet, we’ll get

So, still not what we really want.

The final part is how to get the bullet back in front of the visible links. Easy, as the af:link component has an icon property where can specify an image we use as a bullet. The final page looks like

You can use any other image as an icon to show in front of the link. The missing part is how we got rid of the original bullet from the af:panelList. Simply by using a style class, we defined in a skin file and applying it to the af:panelList

@charset "UTF-8";
/**ADFFaces_Skin_File / DO NOT REMOVE**/
@namespace af "http://xmlns.oracle.com/adf/faces/rich";
@namespace dvt "http://xmlns.oracle.com/dss/adf/faces";

.ivi af|panelList {
    list-style-type: none; 
}

.ivi af|panelList::item {
    list-style-type: none; 
}

And using this page

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE html>
<f:view xmlns:f="http://java.sun.com/jsf/core" xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
    <af:document title="panelList.jsf" id="d1">
        <af:form id="f1">
            <af:panelGridLayout id="pgl1">
                <af:gridRow height="50px" id="gr1">
                    <af:gridCell width="100%" halign="stretch" valign="stretch" id="gc1">
                        <af:outputText value="PanelList with Bullet" id="ot1" inlineStyle="font-size:x-large;"/>
                        <!-- Header -->
                    </af:gridCell>
                </af:gridRow>
                <af:gridRow height="100%" id="gr2">
                    <af:gridCell width="100%" halign="stretch" valign="stretch" id="gc2">
                        <af:panelList rows="5" styleClass="ivi"> 
                            <af:link text="link 1" id="l1" destination="http://www.oracle.com"
                                     icon="/images/bullet.png"/>
                            <af:link text="link 2" id="l2" destination="http://www.oracle.com"
                                     icon="/images/bullet.png"/>
                            <af:spacer width="10" height="10" id="s1"/>
                            <af:link text="link 3" visible="false" id="l3" destination="http://www.oracle.com"
                                     icon="/images/bullet.png"/>
                            <af:link text="link 4" id="l4" destination="http://www.oracle.com"
                                     icon="/images/bullet.png"/>
                        </af:panelList>
                    </af:gridCell>
                </af:gridRow>
            </af:panelGridLayout>
        </af:form>
    </af:document>
</f:view>

You can download the sample which was built using JDev 12.2.1.3 from GitHub BlogPanelList. The sample doesn’t need any DB connection or model project.

Advertisements

JDeveloper 12.2.1.3: REST POST Sample

Lately, I got a request to build a REST POST sample using an ADF REST DataControl. Well, here we go.

First of all, we need a REST API which allows us to create data as this will be translated to REST POST call. I deliberately don’t want to use an ADF based REST service as there are samples available for this.

Looking for free REST API services which allow creating data I found “reqres’ (http://reqres.in), a free ‘hosted REST-API ready to respond to your AJAX requests’. This service can be used to test REST calls using any verb you like. It promises to be online 24/7.

Let’s start by creating a new ADF Web Application. If we would only test the REST service we could have created a custom application, but I want to show the viewController part too, so the ADF Web Application is just fine.

I don’t show how to do this here as you can see it done here ‘Why and how to write reproducible test cases’

As we don’t use the ADFModel project you can delete this empty project if you like. After creating the initial workspace, we create a new project from the gallery as a ‘Custom Project’

I named the new project ‘BRPWebService’, but you can name the project anything you like. Inside the new project we new create a ‘Web Service Data Control (SOAP/REST)’:

After selecting this, a wizard will ask which kind of ‘Web Service Data Control’ we like to create. We choose REST and now have to specify the base URL to the REST service API.

We skip the next page as there is no OWSM Policy needed to access the REST API

In step 3 we define the path we want to use after the base URL. From the web page of the service, we see a bunch of possible API endpoints.

For this test, we use the ‘api/users’ path. The whole URL now is ‘http://reqres.in/api/users’. To test the creation of data we use the POST verb, so we select the POST. To make sure the service is functional, we add the GET verb too.

After naming the methods getUsers for the GET and createUser for the POST, we need to give the wizard info about the parameters the API expects and the response we get when the call is successful. When we click on the GET verb on the web page, we get all the info we need.

All JSON code samples are available from the web page or from this blog in the appendix

We copy the response from the web page and pate it o the ‘Response Sample’ field and create a parameter ‘page’ in the parameter section of the next wizard step.

The same we do for the createUser method. Here we have to copy the request parameter JSON and the response JSON from the web page.

On the next page, we test the Web Service Data Control

This completes the creation process. We can now run the Datacontrol from inside JDeveloper by right-clicking the data control and choosing ‘Run’

Testing the getUsers method with the parameter set to 2 we receive the right answer

Now, let’s try the POST verb by trying out the createUser method. As a parameter, we pass some JSON and after executing the method we get a JSON part back telling us the new Id of the user.

Everything works. Please remember that we only use a ‘fake’ service which accepts our JSON, but will not add any data!

Sample Application

For the fun of it, I developed a ViewControler which uses the REST Data Control. It allows to ask for users by providing a page to load, or you can test the POST verb to create a ‘fake’ new user.

The final application can be downloaded from GitHub BlogRestPost. It was built using JDeveloper 12.2.1.3 without a DB connection.

After starting the app we see this UI

The service allows getting users in page mode, three users per page. So, enter 3 into the ‘Page’ filed and we get

Now, filling ‘name’ and ‘job’ field and clicking the createUser button we get

The service returns a new user with a new ID and the timestamp when the user was created.

Appendix

JSON reponse for GET api/users?page=2

{
    "page": 2,
    "per_page": 3,
    "total": 12,
    "total_pages": 4,
    "data": [
        {
            "id": 4,
            "first_name": "Eve",
            "last_name": "Holt",
            "avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/marcoramires/128.jpg"
        },
        {
            "id": 5,
            "first_name": "Charles",
            "last_name": "Morris",
            "avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/stephenmoon/128.jpg"
        },
        {
            "id": 6,
            "first_name": "Tracey",
            "last_name": "Ramos",
            "avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/bigmancho/128.jpg"
        }
    ]
}

JSON payload and response for POST api/create

Payload
{
    "name": "morpheus",
    "job": "leader"
}

Reponse
{
    "name": "morpheus",
    "job": "leader",
    "id": "783",
    "createdAt": "2018-11-19T21:42:47.556Z"
}

ADF TreeTable Advanced Sample

Building a tree table isn’t straightforward if you want an appealing design. By design, I don’t mean something fancy with different fonts or colors, but I’m talking about the definition of the tree and its sub-notes.

Look at an af:treetable which is generated automatically for you using the Departments and Employees tables of the HR DB.

<af:treeTable value="#{bindings.DepartmentsView1.treeModel}" var="node"
              selectionListener="#{bindings.DepartmentsView1.treeModel.makeCurrent}"
              rowSelection="single" id="tt1">
    <f:facet name="nodeStamp">
        <af:column id="c1">
            <af:outputText value="#{node}" id="ot2"/>
        </af:column>
    </f:facet>
    <f:facet name="pathStamp">
        <af:outputText value="#{node}" id="ot3"/>
    </f:facet>
</af:treeTable>

This code, when put inside a simple page with an af:panelColletion, will produce this output:

As you see, there is only one column. You would not even know that you look at an af:treeTable. OK, the af:treeTable binding used is very simple:

For the first level and for the second level

In the remainder of this post, I show how to build a better-looking version with columns for the different data shown.

As usual, you can download the sample code from GitHub. See the link at the end of this post.

Small changes

To see the whole information in the one tree table column, we change the size of the column to 200px

<af:treeTable value="#{bindings.DepartmentsView1.treeModel}" var="node"
              selectionListener="#{bindings.DepartmentsView1.treeModel.makeCurrent}"
              rowSelection="single" id="tt1">
    <f:facet name="nodeStamp">
        <af:column id="c1" width=”200”>
            <af:outputText value="#{node}" id="ot2"/>
        </af:column>
    </f:facet>
    <f:facet name="pathStamp">
        <af:outputText value="#{node}" id="ot3"/>
    </f:facet>
</af:treeTable>

to get this output which shows all data, but still all in one column:

nodeStamp and pathStamp

When you look at the generated af:treeTable tag you’ll notice two facets:

nodeStamp and pathStamp

 

The nodeStamp facet is used to render the tree table and lets you drill down the data defined in the binding of the tree table.

To render the tree table a special EL, normally ‘#{node}’, is used to stamp out the data of each level. Which data is stamped is defined in the tree binding. We defined to show the DepartmentName name for the first level and EmployeeId, LastName and FirstName for the second level. This is why we get the output in the image above.

In summary: the nodeStamp renders the first column of the tree table.

The pathStamp is used for navigation inside the af:treeTable like when you select a node and make it the top node of the tree or tree table. For more info on this check the documentation.

Building columns:

An af:treeTable, like a table, can and should have columns. As you see in the image above, the af:treeTable looks like a normal tree when you don’t define columns yourself.

Columns are defined outside the ‘nodeStamp’ facet (and pathStamp facet) of an af:treeTable. The tag doc tells us:

Columns

Like the Table, the TreeTable’s children must be Column components (see Table Columns). Like the Tree, the TreeTable has a “nodeStamp” facet which renders the “Object Name” Column. The “Object Name” Column contains the primary identifier of an element in the hierarchy. For example, in an organization chart of employees, the “Object Name” Column might be the employee name.

 

Sample: to show columns for Lastname and FirstName of an Employee we add af:column tags like

<af:treeTable value="#{bindings.DepartmentsView1.treeModel}" var="node"
              selectionListener="#{bindings.DepartmentsView1.treeModel.makeCurrent}"
              rowSelection="single" id="tt1">
    <f:facet name="nodeStamp">
        <af:column id="c1" width=”200”>
            <af:outputText value="#{node}" id="ot2"/>
        </af:column>
    </f:facet>
    <f:facet name="pathStamp">
        <af:outputText value="#{node}" id="ot3"/>
    </f:facet>
    <af:column id="c2" width="200">
        <af:outputText value="#{node.LastName}" id="ot4"/>
    </af:column>
    <af:column id="c3">
        <af:outputText value="#{node.FirstName}" id="ot3"/>
    </af:column>
</af:treeTable>

outside the nodeStamp facet. This will get us this output

Now we have a problem. The second level nodeStamp prints out all attributes of hte node. This is exactly what the EL ‘#{node}’ does. The first level doesn’t have this problem as we only defined one attribute ‘DepartmentName’ for this node.

We don’t like to see the sting in the 2nd level. There exist two solutions to solve this problem.

  1. We use the EL ‘#{node.DepartmentName}’ instead of ‘#{node}’. As the attribute DepartmentName doesn’t exist in level 2, the EL evaluates to nothing (or empty string). However, keep in mind that this only works if the attribute only exists in level one.

  1. We define an EL for the node depending on their level. This solution will work in all cases. To implement this we use an af:switcher component which we use to find out which level we are currently stamping out. This allows us to use attributes defined for the level and build some output for the nodeStamp. Details for this solution follows below.

How to find out which node level currently is stamped out?

One easy way is to look at the view objected used for the level. If you are using some other source you can add an attribute (static) for each level which can then be used in the EL for the af:switcher.

To find out the name of The view object used we can look at the source of the tree binding in the pagedef

  <bindings>
    <tree IterBinding="DepartmentsView1Iterator" id="DepartmentsView1">
      <nodeDefinition DefName="de.hahn.blog.treetable.model.views.DepartmentsView" Name="DepartmentsView10">
        <AttrNames>
          <Item Value="DepartmentName"/>
        </AttrNames>
        <Accessors>
          <Item Value="EmployeesView"/>
        </Accessors>
      </nodeDefinition>
      <nodeDefinition DefName="de.hahn.blog.treetable.model.views.EmployeesView" Name="DepartmentsView11">
        <AttrNames>
          <Item Value="EmployeeId"/>
          <Item Value="LastName"/>
          <Item Value="FirstName"/>
        </AttrNames>
      </nodeDefinition>
    </tree>
  </bindings>

The name we are looking for is the DefName of the nodeDefinition. But how do we access this information in an EL?

Frank Nimphius blogged about this here https://blogs.oracle.com/jdevotnharvest/how-to-determine-the-adf-tree-node-type-using-el. We use the EL

#{node.hierTypeBinding.viewDefName}

which returns the nodes DefName from the binding.

With this info, we can build the af:treeTable

<af:treeTable value="#{bindings.DepartmentsView1.treeModel}" var="node"
              selectionListener="#{bindings.DepartmentsView1.treeModel.makeCurrent}"
              rowSelection="single" id="tt1">
    <f:facet name="nodeStamp">
        <af:column id="c1" width="200">
            <af:switcher facetName="#{node.hierTypeBinding.viewDefName}"
                         defaultFacet="default" id="sw1">
                <f:facet name="de.hahn.blog.treetable.model.views.DepartmentsView">
                    <af:outputText value="#{node}" id="ot2"/>
                </f:facet>
                <f:facet name="de.hahn.blog.treetable.model.views.EmployeesView">
                    <af:outputText value="ID: #{node.EmployeeId}" id="ot11"/>
                </f:facet>
                <f:facet name="default">
                    <!-- use this facet if the other facets don't match! -->
                    <af:outputText value="#{node}+++++#{node.hierTypeBinding.viewDefName}"
                                   id="otd11"/>
                </f:facet>
            </af:switcher>
        </af:column>
    </f:facet>
    <af:column id="c2" width="200">
        <af:outputText value="#{node.LastName}" id="ot4"/>
    </af:column>
    <af:column id="c3">
        <af:outputText value="#{node.FirstName}" id="ot3"/>
    </af:column>
</af:treeTable>

to get this output

Using the same technique in the columns allows showing even more information.

How about showing the manager of each department next to the department in the column where the employees are shown?

Easy, after changing the DeparmentView to return the manager info together with the department

and adding attributes for MgrEmployeeId, MgrLastName and MgrFirstName we can implement this using the following code

<af:treeTable value="#{bindings.DepartmentsView1.treeModel}" var="node"
              selectionListener="#{bindings.DepartmentsView1.treeModel.makeCurrent}"
              rowSelection="single" id="tt1">
    <f:facet name="nodeStamp">
        <af:column id="c1" width="200">
            <af:switcher facetName="#{node.hierTypeBinding.viewDefName}"
                         defaultFacet="default" id="sw1">
                <f:facet name="de.hahn.blog.treetable.model.views.DepartmentsView">
                    <af:outputText value="#{node.DepartmentName}" id="ot2"/>
                </f:facet>
                <f:facet name="de.hahn.blog.treetable.model.views.EmployeesView">
                    <af:outputText value="ID: #{node.EmployeeId}" id="ot11"/>
                </f:facet>
                <f:facet name="default">
                    <!-- use this facet if the other facets don't match! -->
                    <af:outputText value="#{node}+++++#{node.hierTypeBinding.viewDefName}"
                                   id="otd11"/>
                </f:facet>
            </af:switcher>
        </af:column>
    </f:facet>
    <af:column id="c2" width="200">
        <af:switcher facetName="#{node.hierTypeBinding.viewDefName}" defaultFacet="default"
                     id="sw2">
            <f:facet name="de.hahn.blog.treetable.model.views.DepartmentsView">
                <af:outputText value="#{not empty node.MgrLastName ? 'Manager: ' : ''}"
                               id="ot5" inlineStyle="font-weight:bold;"/>
                <af:outputText value="#{not empty node.MgrLastName ? node.MgrLastName : ''}"
                               id="ot6"/>
                <af:outputText value="#{not empty node.MgrLastName ? ', ' : ''}" id="ot4"/>
                <af:outputText value="#{not empty node.MgrLastName ? node.MgrFirstName : ''}"
                               id="ot7"/>
            </f:facet>
            <f:facet name="de.hahn.blog.treetable.model.views.EmployeesView">
                <af:outputText value="#{node.LastName}" id="ot211"/>
            </f:facet>
            <f:facet name="default">
                <!-- use this facet if the other facets don't match! -->
                <af:outputText value="#{node}+++++#{node.hierTypeBinding.viewDefName}"
                               id="otd211"/>
            </f:facet>
        </af:switcher>
    </af:column>
    <af:column id="c3">
        <af:outputText value="#{node.FirstName}" id="ot3"/>
    </af:column>
</af:treeTable>

Will produce this tree table:

You can add more, like icons in each column or …

You can download the sample application from GitHub: BlogTreeTable. The sample was built using JDev 12.2.1.3 but the same technique should work in other JDev versions too (11g or newer).

JDev 12.2.1.3: Creating a shared skin jar (Part 2)

In part 1 or the series we created a simple skin, built an ADF-Library from it and tried to reuse it by deploying it to a WebLogic Server. This approach failed. In this part, we try another option to share a jar with

Sharing the skin with other applications

To share the skin with other application we can

  1. Create an ADF Library
    1. Add this library to the other application
    2. Add this library as a shared library to a WebLogic Server
  2. Create a normal jar
    1. Add this library to the other application
    2. Add this library as a shared library to a WebLogic Server

For this blog the way we want to use if 2b. This allows to create the skin once, deploy it to a server and use it in every other application. In the next paragraph, we try out option 1a to show the problems when reading resources from a jar file.

Using a shared skin

Option 2b

Here we create a jar file containing the skin and additional resources like images and deploy it directly to a WebLogic Server as a shared library. The advantage is, that other applications can use the skin and other resources directly and that the jar can be versioned to allow different versions of the same jar on the server.

Option 1a which we discussed in the previous chapter doesn’t work for images. However, the documentation ‘Deploying a Custom Skin File in a JAR File’ and Frank Nimphius pointed it out in e.g. 86. Reading boilerplate images and icons from a JAR or How-to share skin definition files across applications how the jar file must be structured to allow the resource servlet to read the resources. The essential sentence is

‘All image resources and CSS files must also be under the META-INF directory.’

In his article, Frank suggested using the command line jar tool to create the jar. I show how to use JDev to create the jar with the needed structure and how to deploy it to a server as a shared library.

A sample application is used to use the skin and to show an image load from the jar.

The building plan for a skin in a shared Library which can be deployed to a WebLogic server is given in the article as:

To implement the shared library approach, developers need to change their existing skin definition so it can be deployed in a JAR file. The steps for this include

– Creating a META-INF directory – Creating a trinidad-skins.xml file that defines the skins deployed with the JAR file

– Creating an META-INF/adf sub directory for images and icons served from the JAR file

– Changing the image reference in the CSS to include the “adf” directory, which makes sure images and icons are handled by the ADF Faces resource loader, which can read resources from JAR files

– JAR the META-INF directory to create the library file

Looking at the current project for the skin we see a different layout

In the article Frank instructed to create the needed folders yourself and copying or moving the files to the new structure, then to use the command line to build a jar from the structure.

I’ll show how this can be done with a special deployment descriptor from within the project. The image below shows the needed layout of the final jar file.

To transform the folder structure present in JDev to the needed structure of the final jar, we create a new deployment descriptor in JDev

In image 4 we see the first part of the solution: here we set the path inside the jar to ‘MEAT-INF’. This will guarantee the structure we need. Then we add another contributor to the list (public_html) to get everything we need into this folder. Then we use the ‘Filters’ node to select all content we need skin part

Next part is to create another path in the jar for the metadata of the skin

We add another file group for the resources

Now we can deploy the jar using the new deployment descriptor

And the jar file is created in the deploy folder. It holds all files in the right folders

Finally, we can deploy this jar to the WebLogicServer. In this case, I use the integrated WLS, but it can be any stand-alone WLS too.

The error message you see on the 7th image can be ignored. It only tells you that the library can’t be deployed as an application but only as a shared library. This is exactly what we want to do 🙂

Now the jar file is deployed on the WLS as a shared library and can be used for every application on this server.

We use the existing application from part one to consume the jar skin from the shared library and show the images deployed with the jar.

Before we go any further, we have to remove the ADF Library we added to show the problem from the project. For this open the project properties and select the ‘Library/Classpath’ node and remove the ‘ADFLibrary’ entry

The page should now look like no skin is used at all.

As we already added a skin (with the ADF Library) we don’t have to do this again. However, we have to add a library reference to configure the application to use the shared library deployed on the server. For this, we open the application descriptors and edit the ‘weblogic-application.xml’ file by double clicking the entry in the application resources section

In the ‘Shared Library Reference’ section, we add a reference to the now deployed shared jar ‘blogsharedskin’

Saving everything we don’t see any change to the page design, as the library isn’t part of the application yet. Starting the application we get

Just what we liked to see. The images are visible, checking the page with Chrome’s Dev Tools shows that the images are correctly loaded

This proves that the shared library with the skin and the images are working correctly.

To make the skin visible in JDev during development, we can add the jar we developed to the server a library. We create a library

and make sure the ‘Deploy by default’ is NOT set. Adding the library to the project

will make the skin visible in design mode

The unset checkmark prevents the jar from being packed into the WAR or EAR file. It’s just used in the IDE. That you can’t see the images is normal as there is no full server to serve the images to the design view.

Summary

In this mini-series, I showed the problem when creating a skin as ADF Library and trying to share it on a Weblogic Server. Then I showed how to create a deployment descriptor for the skin and other resources and how to deploy the resulting jar to a WebLogic Server.

The sample application can be downloaded from BlogSharedSkin. The sample was created by using JDev 12.2.1.3 but the same technique can be used in any 12.2.1.x JDev version. There is no database connection needed.

JDev 12.2.1.3: Creating a shared skin jar (Part 1)

In earlier versions of JDev, skins have been created either by pure code or by using the free Skin Editor. However, since JDev 12.2.1.x the skin editor has been integrated into JDeveloper itself.

A couple of questions in the ODC JDeveloper space are about how to create a skin with JDev which can be deployed as a shared library to a WebLogic Server. I gave this a try and it turned out, that you can build an ADF library jar from a skin project but you can’t use images to this jar which you might want to use in the application.

In this blog, I’ll show how to create a skin with resources like images and how to build a jar file from the skin together with the images and deploy it aa s shared jar to a WeblogicServer.

Building a skin project

The first part is to build a small skin project. The project we use to create a minimal skin, just to show that the skin is changing something. Then we add some images to the skin which we want to use in the application which uses the skin. Such images can e.g. used on an af:button component.

We start by creating a new application as an ‘ADF Fusion Web Application’

As we don’t need the created model project we delete it completely

If you get another dialog, telling you that you can’t undo the action, answer ‘Yes’ to delete the project. Now you should see a workspace with just the one project:

Know that we have a project we add a skin and e.g. add some skin selectors to change to the color of the button text. For this, we right click the ‘Web Content’ folder in the project and select ‘New from Garaly’ and then select ‘ADF Skin’ from the ‘JSF/Facelets’ node and fill in the basic information:

This will create the needed css file and the descriptors which define our skin (trinidad-config.xml and trinidad-skins.xml).

We open the sharedskin.css file if it’s not open already and switch to source mode. Here we add two simple skin selectors

which are changing the color of the text of a button and a link. You can add more sophisticated selectors but for this blog, it’s enough to show the working skin. To make it more interesting, and because that’s the real reason for this blog, we add some images to the skin which we like to use in the application using the skin. We add the images into a new folder like shown below

The reason for this structure is, that to read the images from the jar in the consuming application, we need a special resource loader. In case of ADF it’s the resource servlet which listens to the URL pattern ‘/adf/’’. This servlet is installed automatically for ADF Web Applications and is configured in the web.xml file

The final task for the skin project is to create a jar file which we can use in other applications. The easiest way to get such a jar is to create an ADF Library deployment descriptor. Open the project properties of the skin project and select the ‘Deployment’ node

And click the ‘New Profile’ icon, select to create an ADF Library Jar

and click ‘OK’. The remaining dialogs you can just click ‘OK’ or ‘Finish’.

To create the library we have to execute the descriptor by right-clicking on the project and selecting ‘Deploy’ and choosing the ‘sharedskinadflib’

This will create the jar in the ‘deploy’ folder of the project.

Sharing the skin with other applications

To share the skin with other application we can

  1. Create an ADF Library
    1. Add this library to the other application
    2. Add this library as a shared library to a WebLogic Server
  2. Create a normal jar
    1. Add this library to the other application
    2. Add this library as a shared library to a WebLogic Server

For this blog, we want to use option 2b. This allows to create the skin once, deploy it to a server and use it in every other application. In the next paragraph, we try out option 1a to show the problems when reading resources from a jar file.

Using a shared skin

Option 1a

We start with option 1a, just to show the problem when we try to read a resource from a jar. We build another ADF Fusion Web application and add the skin as ADF library from a ‘File System Connection’ which we create and let it point to the ‘deploy’ folder

Right-click on the ‘sharedskinadflib’ and add it to the new sample project. This will make the skin available to the application. To use the skin we have to add a skin to the application like we did to create the skin project. The difference is that we now choose the shared skin as the base skin

Creating a new page and adding a button and/or a link to the page we see the new style introduced by the ‘sharedskin’

So, the shared skin is working. Well, yes, but what about the images we added to the ‘sharedskin’?

Let’s try to add one to the button. In the property editor, we select the icon property of the button and click ‘Edit’ to get

However, we don’t see any image in the whole project. As we know where we put the images (or we can look into the sharedskinadflib) we can just add the path to the image like ‘skins/sharedskin/adf/images/home.png’ and we see the image

Running the application we get the page with the button but don’t see the image

Using DeveloperTools we see that the resource couldn’t be found. Inspecting the button element we see

The path to the image is not found 😦

If we change the address of the image to ‘/adf/images/home.png’ to use the resource servlet we still get an error

The reason is that the resource servlet expects the resources in a different path inside the jar. Every resource which should be read from a jar should be in a folder named ‘META-INF’.

The ADF library did not put the images into the META-INF folder

The problem is that we can’t change the layout of the ADF Library. When you create an ADF Library there is no option to make any changes to the content of the jar.

The conclusion is that using a skin in an ADF Library is problematic if there are other resources which you need to share.

To be continued…

In the final part 2 of the series, we see how the skin can be shared with other applications.

JDeveloper: Info about the clicked cell in an af:table

JDeveloper allows to easily create tables with the af:table component. The table allows easy access to the selected row or rows. However, if you are interested in which cell of a table has been clicked, ADF needs some tweaking. This blog is about how to tweak an af:table to get exactly this info.

Use Case

You like to know which cell in an af:table a user has clicked, e.g. to get some detailed information about the clicked item or cell in the selected row. The sample I show get the information about the current row, and column of the cell and the value of the cell clicked. The final sample will show the info like

How to do it?

The normal af:table component doesn’t give information about the cell a user has clicked on. The ADF pivot table offers this but is complex to use.

We use JavaScript in form of a clientListener to intercept the click on a cell and a serverListener to call a bean method to get more data on the cell. This article 011. ADF Faces RC – How-to use the Client and Server Listener Component shows how to use clientListener and serverListener in detail.

As we are interested in the selected cell, we add a clientListerer to each af:outputText which shows the column value in the af:table which fires on the click event. The clientListener calls a JavaScript method. In the JavaScript method, we build a payload of the UIComponent which is used to show the column value and the column name of the cell. To get this information we have several possible ways:

  1. We can use our knowledge of the DOM tree and get the column via the parent of the component which fired the event. The parent component should be the af:column.
  2. We add a client attribute to the component which shows the cell value adding the column name from the af:column as EL

In this sample, we choose the second solution. With this information, we call the serverListener from the JavaScript method. The serverListener method is implemented in a request scope bean and uses the information passed to get the details about the clicked cell we show in the UI.

Implementation

The sample uses the HR DB schema and only needs one table, Employees in this case. We create a simple page with the table in read-only mode, sortable and filterable. As you see in the image above the table is just build be dragging the Employees VO onto the page and drop it as a read-only table.

Now we add a clientListener and a serverListener to each outputText component which is used to show the cell value

In the image above we see the listener for two columns. In addition, we add an af:clientAttribute with the name ‘columnName’ which we pass the EL of the af:column headerText property.

Next, we add an af:resource component to the af:document where we specify the JavaScript for the clientListener method ‘clientCellSelectionCall’. We use a JavaScript file to code the method. We could have added the method to the page directly, but if we want to reuse the pattern, it’s better to use a JavaScript file

The file is located in the public_html folder (Web Content) in a subdirectory ‘javascript’

The method code is

The click event on the af:outputText component triggers a call to the javascript method ‘clientCellSelectionCall’ (via the clientListener) with the source of the event, the af:outputText component. The method reads the clientAttribute added (line 3) and calls the serverListener of type ‘cellSelection’. This event is defined by the af:serverListener on the af:outputText. The component which triggered the event and the column name added as client attribute are passed to the serverListener.

The serverListener is a bean method defined in a request scope bean on the af:outputText component as

method="#{TableCellSelectionBean.handleTableCellSelection}"

In the bean, the method looks like

public void handleTableCellSelection(ClientEvent event) {
  // get payload which is the ui component which fired the event
  UIComponent ui = (UIComponent)event.getParameters().get("payload");
  // get the column from the event which is sent too
  String column = (String)event.getParameters().get("column");
  RichOutputText rt = (RichOutputText)ui;
  // get current row key
  DCBindingContainer bindingContainer = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
  DCIteratorBinding binding = bindingContainer.findIteratorBinding("EmployeesViewIterator");
  Row currentRow = binding.getCurrentRow();
  Key key = currentRow.getKey();
  // compile info about clicked cell
  String out = "Payload:" + ui + "
  column: "+ column + "
  val: " + rt.getValue() + "
  key: "+key.toString();
  logger.info(out);
  setCellInfo(out);
}

Here we get the component which triggered the event (as payload) and the name of the column. Using this information we can get e.g. to the value of the column (via the UI component). The row of the cell we get via the current row of the iterator. With this information, we get the key of the row. We can get much more information here, like historical data about the current employee’s salary, if the salary cell was clicked.

We just create a string from the information which we show in the UI to the user

Here are some images of different cells clicked in the UI:

Download

You can download the sample, which was built using JDeveloper 11.1.1.9, from GitHub BlogTableCellSelection. The sample uses the HR DB schema.

JDev 12c: Debug Application Module Tester (BC4JTester) Problems

When you develop ADF Web Application you often use the ADF ApplicationModule Tester (BC4J Tester) to quickly test your business components data model and your self-written code in any EntityObject, ViewObject or ApplicationModule. For more information about how to do this look at JDeveloper & ADF: Use the Application Module Tester (BC4J Tester) to Test all your BusinessLogic.

Users who use one of the latest JDeveloper versions 12.2.1.1.0 and newer may have noticed, that the BC4J Tester application starts without an error, but doesn’t show the dialog. I run into this a couple of times lately and decided to dig into this problem. On the Oracle Development Spaces, I saw some threads about this too.

The reason for this behavior is that any EO, VO or other methods in the application module have an error, which can’t be found during compile time.

Use case

To show the effect, we start with a simple Workspace and a model project which only has one ViewObject in the Application Module’s data model

We implement a small use case where we want to see the total salary of all rows retrieved by the query behind the VO. Without any added where clause we get the total salary of all employees. If we add a filter e.g. by DepartmentId=90 we only get the total salary of all employees of department 90. Here are some images of the final running model in the BC4J Tester

Implementation

OK, so how do implement this use case?

We do this by adding a transient attribute to the EmployeesView and use a SQL default expression to do the calculation

sum(Employee.SALARY) OVER (PARTITION BY NULL ORDER BY NULL)

In the image below we see the definition of the transient attribute in the ViewObject

Problem

This should do the trick. However, when we try to test this in the BC4J Tester we get

In the log window, but no dialog where we see the application module. We don’t get any hint about what went wrong. The tester is up and running, but we don’t see anything.

Shay Shmeltzer mentioned in one of the ODC threads, that the reason for this is that there is an error in the application module (ViewObject, EntityObject or AM method). As the only thing we added is the SQL statement for the transient attribute, it’s clear that the statement must have an error. It’s simply a missing ‘s’ character, as the DB table we use is named ‘Employees’ and not ‘Employee’. So the correct statement is

sum(Employees.SALARY) OVER (PARTITION BY NULL ORDER BY NULL)

This will solve this problem and the BC4J Tester will start up and show (see the images above). But what if we added more things to multiple objects?

How to find the error then?

Older versions of JDev, the BC4J Tester did show an error message which showed the error and made solving the problem easy. Here is an image of the same application running using JDev 12.1.3.0.0

Solution

I did not manage to get the same output using JDev 12.2.1.1.0 or newer, but you can get the same message in the message window.

For this, you need to start the BC4J Tester with the java option

-Djbo.debugoutput=console

The option is added in the model projects ‘Run/Debug’ option in the project’s properties

Whenever you start the BC4J Tester and don’t get any dialog, you can assume that there is an error in the application module. To find out what the problem is, add the java option to the model project and you get the detailed information in the log window.

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.

Query and Filter an af:listView

Most of the time we use tables to show tabular data to users. However, JDev and ADF allow for other components like the af:listView to be used to show such data to the user in a more modern way.

The image above shows the normal display of data when an af:query is used together with a table to show the result.

A more fancy, modern look we get if we use a af:listView to show the results as this allows us to style the data

Use case 1

We like to use an af:query to search for employees and show the result in a styled af:listView.

Implementation 1

This is pretty easy as we only have to use an af:listView as the result component of the af:query

And to exchange the af:table with an af:listView. Or you build the page by first dropping an af:query onto the page (without table) and then add the af:listView

Then you get the wizard to layout the list

This will give you a basic layout which can be styles in JDev as

The final result is

which looks more modern. One thing the af:table give you out of the box is the second use case.

User Case 2

We like the af:listView to be able to be filter the result like the af:table can.

Implementation of second use case

Easy you think? Well, the af:listView component doesn’t provide any filter out of the box. There isn’t even a filterModel like there is for an af:table.

So, how do we get this implemented. The idea is to use a af:table component but only use the filter provided by the af:table. The remaining parts like table data, possible scroll bars and status bar or scrollbars we remove.

We start by dragging the EmployeesView1 from the data control onto the page again.

And drop it after the closing af:panelHeader and before the af:listView as ‘ADF Table’

In the image you see that I have removed some available columns. Before we go to hide the part of the table we don’t need, we make the table work together with the af:query and the af:listView. When we use the af:query the table shows the right detail (auto PPR triggers the refresh of the table). However, if you have queried for the ‘Purchasing’ department and then enter an ‘s’ into the ‘First Name’ filter field of the table and hit enter, you get

As you see, the table shows the right result (2 rows) but the listView still shows all employees of the Purchasing department.

To make it work, we need to add a partialTrigger to the listView which points to the table. This way each time the table changes the listView will too.

Save all changes and refresh the page. Now if you enter a value into a filter field and hit enter, the listView will update too.

After the page works we have to get rid of the data below the header of the table. This is easy to accomplish by styling the table. We only need the filter field and the header below the filter fields so that we know which field filters which data. Simply set the maxHeight of the table to the exact height of the the two components. You can use your browser’s developer tools (F12) to measure the height. In my sample it’s 65px. So, setting the tables inlinestyle to

max-height: 65px;

will hide everything below the filter and the header

If you like you can create a skin and create a style class and use this style class instead of setting the max-height directly to the inlineStyle of the table. A nice addon is that the table header sorting is working too for the listView.

You can download the sample from gitHub BlogFilterListView. The sample is build using JDev 12.2.1.3 and uses the HR DB schema. The principle can be used in other JDev versions too.

Using External REST Services with JDeveloper Part 3

In this blog we look how we can use an external REST service with JDev 12.2.1.2. To make things more interesting we don’t use an ADF based REST service and we look how to get nested data into the UI.

For this sample we like to create an application which allows to search for music tracks and show the results in a table or listview. To get the music data we use a REST service and to display the data we use ADF faces application.

In Part 1 we create the application and the project for the REST Data Control. In Part 2 we started creating the UI using the REST Data Control. In this final part we are enhancing the UI by using nested data from the REST Web Service and add this as column to the search result table. Before we start we look at the use case again.

Use Case

Before we begin implementing something which uses the external REST service we have to think about the use case. We like to implement a music title search using the external MusicBrainz REST service. A user should be able to enter a music title or part of a music title and as a result of the search she/he should get a list of titles, the artist or artists, the album and an id.

 

Handling nested Data

The use case demands that we add the artist and the album the music track is on to the same table. A look at the table in it’s current layout, make this understandable.

First of all we need to identify the dat a we want to add to the table in the response we get from the service.

Let’s investigate the JSON data, part of it, we get from the service for the search for the track ‘yesterday’


 

{
   "created": "2017-08-02T12:42:48.815Z",
   "count": 5985,
   "offset": 0,
   "recordings": [
       {
           "id": "465ad10d-97c9-4cfd-abd3-b765b0924e0b",
           "score": "100",
           "title": "Yesterday",
           "length": 243560,
           "video": null,
           "artist-credit": [
               {
                   "artist": {
                       "id": "351d8bdf-33a1-45e2-8c04-c85fad20da55",
                       "name": "Sarah Vaughan",
                       "sort-name": "Vaughan, Sarah",
                       "aliases": [
                           {
                               "sort-name": "Sarah Vahghan",
                               "name": "Sarah Vahghan",
                               "locale": null,
                               "type": null,
                               "primary": null,
                               "begin-date": null,
                               "end-date": null
                           },
...
                       ]
                   }
               }
           ],
           "releases": [
               {
                   "id": "f088ce44-62fb-4c68-a1e3-e2975eb87f52",
                   "title": "Songs of the Beatles",
                   "status": "Official",
                   "release-group": {
                       "id": "5e4838fa-07f1-3b93-8c9d-e7107774108b",
                       "primary-type": "Album"
                   },
                   "country": "US",

I marked the info ne need in blue in the data above. We see that the artist name is inside a list of name ‘artist_credit’ and that there can be multiple artists inside the ‘artist_credit’. This is a typical master/detail relationship.

The same is true for the album name which is an attribute inside a list of ‘releases’. The big question now is how do we get the nested data into the table as column.

When we expand the MusicBrainz Data Control we see the same structure we saw in the JSON data

So, the data is there, we only need to get to it. The data is structured like a tree and ADF is capable of accessing data in a tree structure (e.g. using an af:tree component). However, we like to use a simple table and don’t want to use a af:tree or af:treeTable. To get to the data, we first have to add the nested structure to the recordings binding we already use to display the current two columns of the table.

Right now we see the first level of the tree, the ‘recodrings’. Click the green ‘+’ sign to add the next level ‘artist_credit’

Add all attributes to the right side

As the artist name is still one level down, click the green ‘+’ sign again and add the ‘artist’ level

And shuffle the id and name attribute to the right side

Finnally we need to add the ‘releases’ level to get to the album name. For this select the ‘recordings’ level (the first) and click the green ‘+’ sign

And shuffle the id, title and track_count to the right side

Now all related data we need can be accessed via the ‘recordings’ binding.

We start with the artist column. Select the af:table in the structure window and open hte properties window

Click the green ‘+’ sign twice in the columns section to add two columns

Select the first added column (score in the image) and change the display label to ‘Artist’ and the component To Use’ to ‘ADF Output Text’. The second added column we change the display label to ‘Album’ and the ‘Component To Use’ again to ‘ADF Output Text’

We change the ‘Value Binding’ in the next step.

To get to the data for the artists we need to traverse two levels of sub rows. First level is the ‘artist_credit’, the second level is the artist itself. Here we have to keep in mind, that there can be more than one artist. In this case we have to join the names into one string for the table. As the ‘artist_credit’ itself can occur more than once, at least that’S what the data structure is telling us, we use an iterator to get the data.

The value property points to the current row and selects the ‘artist_creadit’. Each item we get from this iterator we access via the var property. So the item inside the iterator can be addressed as ‘artists’.

The artists can be one or more so we need another iterator to get to the artist data.

<af:iterator id="i2" value="#{artists.artist}" var="art" varStatus="artStat">

The value property for this iterator points to the artist we got from the outer iterator and is addressed as #{artists.artist}. To access attributes inside the artist data structure we use the var property and set it to ‘art’.

Now we have to somehow joint multiple artist names together if a track has more than one artist. The MusicBrainz Web Service helps us here by providing a ‘joinphrase’ which can be used to build one string for all artists. This ‘joinphrase’ can be .e.g a ‘&’ or a ‘,’. The full column code for the artist looks like

<af:iterator id="i2" value="#{artists.artist}" var="art" varStatus="artStat">

Here is some sample data for a search for the track ‘Something Stupid’ (to make it more readable I removed some attributes

"recordings": [
 {
  "title": "Something Stupid",
  "artist-credit": [
   {
    "joinphrase": " duet with ",
    "artist": {
     "name": "The Mavericks",
    }
   },
   {
    "joinphrase": " & ",
    "artist": {
     "name": "Raul Malo",
    }
   },
   {
    "artist": {
     "name": "Trisha Yearwood",
    }
   }
 ]

This data will be translated into the artist: “The Mavericks duet with Raul Malo & Trisha Yearwood”.

For the album column it’s easier. This too needs an iterator, but we don’t have to go down another level and we don’T have to join the data we get from the iterator. The column code for the album looks like

<af:iterator id="i1" value="#{row.artist_credit}" var="artists">
 <af:iterator id="i2" value="#{artists.artist}" var="art"
                    varStatus="artStat">
   <af:outputText value="#{art.name}#{artists.joinphrase}" id="ot5"/>
 </af:iterator>
</af:iterator>

The whole table for the search results look like

With this the page is ready and we can run the application. After start we see the page

Now entering a search term ‘something stupid’ into the search field will show

or trying the search with ‘dave’ will show

This concludes this mini series about how to use external REST Services and build an ADF UI from it.

The source code for this sample can be loaded from GitHub BlogUsingExternalREST. The sample was done using JDeveloper 12.2.1.2 and don’t use a DB.