JDeveloper: Advanced Skin Technique

This post is about an advanced technique to change the look and feel of an ADF application. Changes to the look & feel are normally done via a skin which you use to change descriptors which are used by the ADF components. The general technique to do this is described in many blogs and articles like ADF Faces Skin Editor – How to Work with It and the official documentation at Oracle ADF Skin Editor.

In this blog we look at an advanced technique which helps to change the look and feel of components like af:query and pf:panelCollection which you can’t change using the normal available descriptors. In the below image you see the Skin Editor showing the ADF components skin descriptors.

selection_985

Use Case

In this use case we work with the af:panelCollection component. This component is used to wrap af:tree, af:treeTable and af:table components to provide additional functions. From the documentation of af:panelCollection

A panel component that aggregates collection components like table, treeTable and tree to display standard/application menus, toolbars and statusbar items.

The default top level menu and toolbar items vary depending on the component used as the child of the panelCollection.

  • For table, tree and treeTable, the default top level menu item is View.
  • For table and treeTable with selectable columns, the default top level menu items are View and Format.
  • For table and treeTable, the default toolbar item is Detach.
  • For table and treeTable with selectable columns, the default top level toolbar items are Freeze, Detach and Wrap.
  • For tree and treeTable, if the pathStamp facet is used, the toolbar buttons Go Up, Go To Top, Show as Top also appear.

The component allows us to switch off some function

Value Turns off
statusBar Status bar
viewMenu ‘View’ menu
formatMenu ‘Format’ menu
columnsMenuItem ‘Columns’ sub-menu item
columnsMenuItem:col1,col20 Columns with column ID: ‘col1’ and ‘col20’ inside ‘Columns’ sub-menu
freezeMenuItem ‘Freeze’ menu item
detachMenuItem ‘Detach’ menu item
sortMenuItem ‘Sort’ menu item
reorderColumnsMenuItem ‘Reorder Columns’ menu item
resizeColumnsMenuItem ‘Resize Columns’ menu item
wrapMenuItem ‘Wrap’ menu item
showAsTopMenuItem Tree/TreeTable ‘Show As Top’ menu item
scrollToFirstMenuItem Tree/TreeTable ‘Scroll To First’ menu item
scrollToLastMenuItem Tree/TreeTable ‘Scroll To Last’ menu item
freezeToolbarItem ‘Freeze’ toolbar item
detachToolbarItem ‘Detach’ toolbar item
wrapToolbarItem ‘Wrap’ toolbar item
showAsTopToolbarItem Tree/TreeTable ‘Show As Top’ toolbar item
wrap ‘Wrap’ menu and toolbar items
freeze ‘Freeze’ menu and toolbar items
detach ‘Detach’ menu and toolbar items

As a sample the image below shows a normal af:panelCollection (upper half) and an af:panelCollection with the view menu and the toolbar switched off (lower half)

selection_979

Looking at the possible things to switch off we don’t see anything to switch off the ‘Query by Example’ (QBE) icon. There is no feature toggle to turn this function on or off. An easy way to get rid of the icon would be to make the table not filterable. However, if we like the table to be filterable but don’t want to show the icon to switch the feature off, we have to use an advanced skin technique.

What can we do to get rid of the icon in the tool bar?

The idea is to use a skin or special css to hide the icon or the container which holds the icon. To find the container we first inspect the page in the browser using the browsers ‘Developer Tools’ which you can reach by hitting F12 in your browser. Below you see Chrome 55 with activated ‘Developer Tools’

Selection_211.jpg

The image shows the toolbars QBE image as selected element on the page (left red rectangle) and the style classes which are in use for this element (right red rectangle). The names ‘.xfo’ and ‘.xfr’ are the names of the style classes. They are minimized to reduce the download size of the page, but they are not ‘readable’. 

The first thing to do is to make the names ‘readable’ for us. We need to know which skin selector generated the style class. For this we set a context parameter in the web.xml file

 <context-param>
 <param-name>org.apache.myfaces.trinidad.DISABLE_CONTENT_COMPRESSION</param-name>
 <param-value>true</param-value>
 </context-param>

Setting this parameter to true will show us the clear names. The image below shows the same selection only this time with the real names

selection_212

One other nice feature of the ‘Developer Tools’ is that you can inspect elements by just hover over them on the page. This allow us to easily find the element we want to hide via css. Click on the icon marked in hte below image

selection_213

and move the mouse cursor over the page. You see the HTML and the active styles of the element under the cursor. This feature we use to find an element which holds the icon we want to hide and which we can address via css .

selection_214

CSS allows us to address elements inside a skin selector.  For this you need to know the skin selector, the tag or container and it’s ID inside the selector you want to address. In the image above we see the ID of the icon container we want to hide as “id=’pc1:_qbeTbr'” and the container or tag itself which is a ‘div’. The skin selector is the af|panelCollection. With this information we can can change the style attached to the ‘div’ container with the id ‘*_qbeTbr’  in the af|panelCollection as

af|panelCollection div[id$='_qbeTbr'] {
    display: none;
}

This we can add to our skin.css file. However, if we just add it this way it’s changing all af:panelCollection in our application.  If we want this only to be active for specific af:panelColletion we can add a style class name like

af|panelCollection.myPCClass div[id$='_qbeTbr'] {
    display: none;
}

Now we can add the stale class name ‘myPCClass’ to the af:panelCollection when we like the QBE icon not to be shown

 <af:panelCollection id="pc1" styleClass="myPCClass">
   <f:facet name="menus"/>
   <f:facet name="toolbar"/>
   <af:table value="#{bindings.EmployeesView1.collectionModel}" ...
   ...
 <af:panelCollection id="pc2" featuresOff="detachToolbarItem viewMenu">
 <f:facet name="menus"/>
 <f:facet name="toolbar"/>
 ...

will generate this UI output

selection_217

 

As we see, the QBE icon is gone. In the original page we have placed two af:panelCollection components. As you added the new style class only to one of them, the other QBE icon is still visible.

Extending

You can use hte same technique for other complex ADF components like af:query. Here you can style the save button which normally not  supported.

Download

You can download the sample which is build using JDev 12.2.1.2.0  and uses the HR DB schema from GitHub BlogAdvancedSkin

Advertisements

Pimp up an af:query to show the result table in an af:panelCollection

A user on the JDev Forum asked an interesting question on how to show the result of an af:query (e.g. dropped as ‘Query Panel with Table’).
A quick check showed that dropping a named criteria as ‘Query Panel with Table’ produces af:panelGroupLayout containing a af:panelHeader for the af:query component and an af:table for the result of the query.
Here is a sample of a drop as ‘Query Panel with Table’ (some details are omitted to save space):

                    <af:panelGroupLayout layout="vertical" id="pgl1">
                        <af:panelHeader text="Employees" id="ph1">
                            <af:query id="qryId1" headerText="Search" disclosed="true"...
                                      resultComponentId="::resId1"/>
                        </af:panelHeader>
                        <af:table id="resId1" value="#{bindings.EmployeesView1.collectionModel}" var="row"
                                  rows="#{bindings.EmployeesView1.rangeSize}" ...>
                            <af:column ...>
                            </af:column>
                            <af:column ...>
                            </af:column>
                            ...
                        </af:table>
                    </af:panelGroupLayout>

The user asked for functionality like hiding columns or detachment of the table to get a full screen mode. In short he wanted to use the functions of a af:panelCollection together with the automatic setup of af:query and the result table.

This can easily be done by surrounding the af:table component by a af:panelCollection and rewiring the ‘resultComponentId’ property of the af:query, as the af:panelCollection is a naming container.
In JDev select the table with a right click and select ‘Surround with…’

Surround Table with panel collection

and search for the ‘Panel Collection’

Select 'Panel Collection'

and click ‘OK’ to finish this step.
Now we need to change the ‘resultComponentId’ property of the af:query component to account for new the naming container which is added through the af:panelCollection component.
Select the af:query in the design view or in the structure window and open the property inspectors common tab

Change af:query
As you can see JDev shows an error for the ‘resultComponentId’ property as the target has changes its naming container. To change it click the small arrow down at the right hand side of the property, select ‘Edit…’. In the next Dialog look for the af:table component inside the af:panelCollection and select it. Click ‘OK’ to finish the change.
the resulting code looks like (some details are omitted to save space):

                    <af:panelGroupLayout layout="vertical" id="pgl1">
                        <af:panelHeader text="Employees" id="ph1">
                            <af:query id="qryId1" headerText="Search" disclosed="true"
                                      ...
                                      resultComponentId="::pc1:resId1"/>
                        </af:panelHeader>
                        <af:panelCollection id="pc1">
                            <f:facet name="menus"/>
                            <f:facet name="toolbar"/>
                            <f:facet name="statusbar"/>
                            <af:table value="#{bindings.EmployeesView1.collectionModel}" var="row"
                                      ...
                                      rowSelection="single" id="resId1">
                                <af:column sortProperty="#{bindings.EmployeesView1.hints.EmployeeId.name}"
                                           ...
                                </af:column>
                                ...
                                <af:column sortProperty="#{bindings.EmployeesView1.hints.PhoneNumber.name}"
                                ...
                                </af:column>
                            </af:table>
                        </af:panelCollection>
                    </af:panelGroupLayout>

When you run the code you are able to detach the table and hide columns

Resulting page