The Oracle Alta UI design patterns offer many new approaches for navigation in your application as you can see in the navigation patterns section. One of those is the Tree Navigation pattern - which is an updated approach to the way that many applications display menus today.
While the "old" way of building these menus was using the tree component, the new design uses an interface that works better on mobile devices and is easier on the eyes. It uses animation to do in-place replacement of one level in the menu with the next one.
old | new |
![]() | ![]() |
You could also use this approach to represent other types of hierarchical/master-detail relationships.
In the demo below I show you how to quickly implement such navigation pattern with ADF Faces and a combination of af:listView components along with the af:deck component.
There are a bunch of further things you might want to do in your actual application beyond what the demo does.
One is to show on the right side of the page the information on the object you select on the left side. Using a deck component there you can also switch that section to show either Dept or Emp data in the same area. You'll already have the actionListener in place to do the switch of display, and ADF already has the right record selected - so just dropping the same data control on the right as a form will be enough.
Another nice enhancement would be to condition the showing of the right caret to be based on whether there are actually details. This should be easy to achieve with a calculated attribute using groovy - as shown here.
In the demo I also show how to invoke the makeCurrent row selection functionality from a managed bean, this allows me to do two operations when a menu option is selected. The code I use ,which is based on code I found on Ashish's blog, is:
public void deptSelect(SelectionEvent selectionEvent) { ELContext elcontext = FacesContext.getCurrentInstance().getELContext(); MethodExpression methodExpression = FacesContext.getCurrentInstance().getApplication().getExpressionFactory().createMethodExpression(elcontext, "#{bindings.DepartmentsView1.treeModel.makeCurrent}", Object.class, new Class[] { SelectionEvent.class }); methodExpression.invoke(elcontext, new Object[] { selectionEvent }); deck.setDisplayedChild("pgl2"); AdfFacesContext.getCurrentInstance().addPartialTarget(deck); }
I also use styleClass="AFAppNavbarButton" for the "back" button to make it look a bit better.
The full source of the JSF page is:
<?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="untitled3.jsf" id="d1"> <af:messages id="m1"/> <af:form id="f1"> <af:pageTemplate viewId="/oracle/templates/tabletFirstTemplate.jspx" id="pt1"> <f:facet name="header"/> <f:facet name="status"/> <f:facet name="appNav"/> <f:facet name="globalLinks"/> <f:facet name="footer"/> <f:facet name="center"/> <f:facet name="start"> <af:deck id="d2" binding="#{mb3.deck}" displayedChild="pgl1"> <af:panelGroupLayout id="pgl1"> <af:listView value="#{bindings.DepartmentsView1.collectionModel}" var="item" emptyText="#{bindings.DepartmentsView1.viewable ? 'No data to display.' : 'Access Denied.'}" fetchSize="#{bindings.DepartmentsView1.rangeSize}" id="lv1" selection="single" selectionListener="#{mb3.deptSelect}"> <af:listItem id="li1"> <af:panelGridLayout id="pgl3"> <af:gridRow marginTop="5px" height="auto" marginBottom="5px" id="gr1"> <af:gridCell marginStart="5px" width="80%" id="gc1"> <af:outputFormatted value="#{item.bindings.DepartmentName.inputValue}" id="of1"/> </af:gridCell> <af:gridCell marginStart="5px" width="20%" marginEnd="5px" id="gc2"> <af:image source="func_caretright_16_ena.png" id="i1"/> </af:gridCell> </af:gridRow> </af:panelGridLayout> </af:listItem> </af:listView> </af:panelGroupLayout> <af:panelGroupLayout id="pgl2"> <af:button text="#{bindings.DepartmentName.inputValue}" id="b1" actionListener="#{mb3.backToDept}" styleClass="AFAppNavbarButton" icon="/func_caretleft_16_ena.png"/> <af:listView value="#{bindings.EmployeesView4.collectionModel}" var="item" emptyText="#{bindings.EmployeesView4.viewable ? 'No data to display.' : 'Access Denied.'}" fetchSize="#{bindings.EmployeesView4.rangeSize}" id="lv2"> <af:listItem id="li2"> <af:panelGridLayout id="pgl4"> <af:gridRow marginTop="5px" height="auto" marginBottom="5px" id="gr2"> <af:gridCell marginStart="5px" width="80%" id="gc3"> <af:outputFormatted value="#{item.bindings.LastName.inputValue}" id="of2"/> </af:gridCell> <af:gridCell marginStart="5px" width="20%" marginEnd="5px" id="gc4"> <af:image source="func_caretright_16_ena.png" id="i2"/> </af:gridCell> </af:gridRow> </af:panelGridLayout> </af:listItem> </af:listView> </af:panelGroupLayout> <af:transition triggerType="forwardNavigate" transition="slideLeft"/> <af:transition triggerType="backNavigate" transition="slideRight"/> </af:deck> </f:facet> <f:facet name="end"/> <f:attribute name="endWidth" value="0px"/> <f:attribute name="startWidth" value="200px"/> </af:pageTemplate> </af:form> </af:document></f:view>