30 Nov 2015

Master-Detail data with ADF List View

Lately the ADF Faces table component has not been considered cool anymore from UI perspective. List View is supposed to be cool today for displaying data collections. It doesn't mean that we shouldn't use af:table at all. In some cases (pretty often :)) a table is far more suitable than a list view. However, the list view component looks really nice, it fits well with the modern UI tendency and it definitely deserves to be used widely in ADF applications.

One of the most common use-cases related to data collections is displaying master-detail data. In this post I am going to show what we can do about that with List View.

The List View component provides out-of-the-box functionality to display hierarchical data by the use of groupHeaderStamp facet.
Let's say there is Departments-Employees hierarchy defined in the application:


If we drag the detail collection Employees onto a page and choose ADF List View as a desired component, the wizard will allow us to include a group header containing attributes from the parent collection:


As a result the list view on the page will contain groupHeaderStamp facet with Department attributes:

<af:listView value="#{bindings.Departments.treeModel}" var="item" ...
             fetchSize="#{bindings.Departments.rangeSize}" id="lv1">
  <af:listItem id="li1">
    <af:panelGridLayout id="pgl2">
      <af:gridRow marginTop="5px" height="auto" marginBottom="5px" id="gr1">
        <af:gridCell marginStart="5px" width="34%" id="gc1">
          <af:outputFormatted value="#{item.bindings.EmployeeId.inputValue}"
          id="of1"/>
        </af:gridCell>
        ...
    </af:panelGridLayout>
  </af:listItem>
  <f:facet name="groupHeaderStamp">
    <af:listItem id="li2">
      <af:outputFormatted value="#{item.bindings.DepartmentName.inputValue}"
          id="of6"/>
    </af:listItem>
  </f:facet>
</af:listView>


And it looks like this:


It looks nice, but it is not actually what I need!  My requirement is to implement something like this:



So that each department is represented with a panel containing two tabs: general department info and list of employees. This use-case can be easily implemented using Accessors from the tree model node definition. The tree model for our list view in the page def file looks like this:

<tree IterBinding="DepartmentsIterator" id="Departments">
  <nodeDefinition DefName="DepartmentsDeafultVO" Name="Departments0">
    <AttrNames>
      <Item Value="DepartmentId"/>
      <Item Value="DepartmentName"/>
    </AttrNames>
    <Accessors>
      <Item Value="Employees"/>
    </Accessors>
  </nodeDefinition>
  <nodeDefinition DefName="EmployeesDeafultVO" Name="Departments1">
    <AttrNames>
      <Item Value="EmployeeId"/>
      <Item Value="FirstName"/>
      <Item Value="LastName"/>
      <Item Value="Email"/>
    </AttrNames>
  </nodeDefinition>
</tree>

There is Employees accessor defined in the Departments0 node definition. It uses Employees view link accessor to retrieve detail records for each department row. The structure of detail records is described in the Departments1 node definition.
We are going to make use of this hierarchical tree model like this:

<af:listView value="#{bindings.Departments.treeModel}" var="item" ...>            
  <af:listItem id="li1">
   <af:panelTabbed position="above" id="pt1" childCreation="lazyUncached">
    <af:showDetailItem id="tab1" text="Department">
      <af:panelFormLayout id="pfl1" inlineStyle="height:150px;">
      <af:panelLabelAndMessage label="#{item.bindings.DepartmentId.hints.label}"
                               id="plam1">
        <af:inputText value="#{item.bindings.DepartmentId.inputValue}"id="ot1"/>
      </af:panelLabelAndMessage>
        ...

      </af:panelFormLayout>
    </af:showDetailItem>
    <af:showDetailItem id="sdi1" text="Employees">
     <af:listView value="#{item.Employees}" var="empItem" ...>
      <af:listItem id="li2">
        <af:panelGridLayout id="pgl2">
          <af:gridRow  id="gr1">
            <af:gridCell id="gc1">
       <af:outputFormatted value="#{empItem.bindings.EmployeeId.inputValue}"
                           id="of1"/>
            </af:gridCell>
            ...
        </af:panelGridLayout>
      </af:listItem>
     </af:listView>
    </af:showDetailItem>
    </af:panelTabbed>
  </af:listItem>
</af:listView>

So each list item contains a panel tabbed inside and the Employees tab contains a nested list view (actually it could be whatever, for example a table) referring to the Employees accessor.

The sample application for this post is available here. It requires JDeveloper 12.1.3.

That's it!




22 Nov 2015

Dynamic View Criteria UI Hints

In this post I am going to show how we can manipulate view criteria UI hints (such as Show in List, Search Region Mode, Show Operators,  etc.) dynamically on-the-fly. Let's consider a use case when we need to show or hide a view criteria in the drop down list of af:query component depending on user permissions.

Actually, it's very easy. We have to override ViewObjectImpl method getViewCriteria(String name) like this:

@Override
public ViewCriteria getViewCriteria(String name)
{
  ViewCriteria vc = super.getViewCriteria(name);
  if (isCriteriaPermitted(name))
  {
    vc.setProperty(ViewCriteriaHints.CRITERIA_SHOW_IN_LIST,
                   ViewCriteriaHints.CRITERIA_HINT_TRUE);     
  }
  else
  {
    vc.setProperty(ViewCriteriaHints.CRITERIA_SHOW_IN_LIST,
                   ViewCriteriaHints.CRITERIA_HINT_FALSE);
  }
  return vc;
}

isCriteriaPermitted in this code snippet is a custom method returning whether the view criteria is available for the user on af:query component. The getViewCriteria method can be overridden in your base ViewObjectImpl class in order to provide a generic solution for this use case across the application.

That's it!