UIX Developer's Guide |
Contents |
Previous |
Next |
The
TreeBean
,
BrowseMenuBean
and
HGridBean
allow users to browse through complex sets of hierarchical data. This chapter examines these three beans and provide extensive examples on
how to use them.
This chapter contains the following sections:
Some data is naturally tree structured. Consider, for example, the categories, subcategories and items in a shopping application. Let's start with an example of a small tree of data. The top node is "Shop," and under it are the nodes "Books" and "Hardware," which contain their own subnodes.
So what does a tree of data look like in UIX? Let's look at how to build such a tree in uiXML. Each node in the tree is a
DataObject
containing key-value pairs. We will often refer to a node by the value associated with the 'text' key. So we will refer to the root of the tree below as the "Shop" node. Note that uiXML has some special syntax for creating a value that is a
DataObjectList
. In the example below a node queried with the key 'nodes' will return a
DataObjectList
containing its children. So for example when the "Shop" node is queried with the 'nodes' key a
DataObjectList
containing the nodes "Books" and "Hardware" is returned.
Each
DataObject
in the tree may contain any set of key-value pairs. We shall see a little later that certain keys have special meaning. For now let's take the example above and write it in uiXML, with the addition of some seemingly arbitrary key-value pairs.
<nodes text="Shop"
destination="http://www.oracle.com"
destinationText="More Information"
expandable="expanded">
<nodes text="Books"
destination="http://www.oracle.com"
destinationText="More Information"
description="Instructions: buy, buy, buy!"
expandable="expanded">
<nodes text="Sale"
destination="http://www.oracle.com"
expandable="expanded">
<nodes text="Paperbacks"
destination="http://www.oracle.com"/>
</nodes>
<nodes text="Fiction"
destination="http://www.oracle.com"/>
<nodes text="Nonfiction"
destination="http://www.oracle.com"/>
</nodes>
<nodes text="Hardware"
expandable="expanded" >
<nodes text="Desktops"
destination="http://www.oracle.com"/>
<nodes text="Notebooks"
destination="http://www.oracle.com"/>
</nodes>
</nodes>
The
TreeBean
and
BrowseMenuBean
are the UIX Components designed to view such tree-structured data. In fact these two beans can show two different views of the same data source. Let's look at an example of how each bean displays the same data. First let's look at an image of how the above data is viewed in a
TreeBean
.
Now let's look at an image of the same data displayed in a
BrowseMenuBean
when the user is looking at the "Books" node.
Both beans allow the user to navigate through the tree hierarchy. The hierarchy of the data as displayed by the
TreeBean
should be fairly obvious. In the
BrowseMenuBean
the entire hierarchy is not seen, however the user can see the path from the root to the current node in the breadcrumbs at the top. The user can also see the children of the current node as links under the "Categories" and "Items" headers. Links under the "Categories" header are non-leaf nodes, while links under the "Items" header are leaf nodes. Thus when we are looking at the "Books" node we see that the path from the root is "Shop> Books". We also see that the children of "Books" are "Sale", "Fiction", and "Nonfiction" and that the nodes "Fiction" and "Nonfiction" are leaves, while the "Sale" node is not a leaf. Clicking on the links in the breadcrumbs or the links under the header "Categories" should bring the user to a similar page. For example if we clicked on the link that says "Sale" we should see a similar page with the breadcrumbs "Shop> Books> Sale" at the top and the link "Paperbacks" under the "Items" header. Later we will see an interactive version of this example.
Looking at the data we see that in the "Books" node the value associated with the 'description' key is "Instructions: buy, buy, buy!". Looking at the images we see that this text is displayed by the
BrowseMenuBean
but not by the
TreeBean
. Indeed, the
TreeBean
ignores the value for the 'description' key. Similarly the
BrowseMenuBean
ignores the value associated with the 'expandable' key, while the
TreeBean
uses this information to determine whether the children of a node should be displayed. We will discuss the exact keys expected by both beans extensively in later sections.
We will begin with a discussion of the
TreeBean
.
The
TreeBean
supports the following named child:
uiXML Key | UIConstant | Description |
---|---|---|
nodeStamp
|
NODE_STAMP_CHILD
|
Bean to render once for each
|
The
TreeBean
supports the following attributes:
uiXML Key | UIConstant | Type | Description |
---|---|---|---|
id
|
ID_ATTR
|
String
|
A page-wide unique id. |
formName
|
FORM_NAME_ATTR
|
String
|
The name of the form to submit. |
formSubmitted
|
FORM_SUBMITTED_ATTR
|
Boolean
|
Whether or not to use form submission. Form submission is not the default, thus if form submission is desired then this attribute must be explicitly set to true. |
The
TreeBean
supports the following children which have a special attribute syntax. When used with the attribute syntax they must be databound.
uiXML Key | UIConstant | Type | Description |
---|---|---|---|
nodes
|
NODES_ATTR
|
DataObjectList
|
The tree of data. This
|
proxy
|
PROXY_ATTR
|
TreeDataProxy
|
The proxy keeps track of the expanded/collapsed state of the tree by setting up the expand and collapse destinations. It optionally tracks the currently selected node in the tree. |
Let's start with a look at the most basic tree.
In this example and those that follow some of the actual UIX is left out in order to focus attention on the issue at hand, however the examples themselves have a link to view the full source.
<tree/>
This example is so basic you don't see a thing. In it, we've only declared that we want to show a tree element, however in order to display something we need to add some data.
The tree data is set with the
NODES
attribute.
Not surprisingly, the data for a
TreeBean
is itself in tree form. The nodes in this tree of data are
DataObject
s. Each
DataObject
is expected to store certain data at specific keys. The following table describes the key-value pairs expected in each
DataObject
.
uiXML Key | UIConstant | Type | Description |
---|---|---|---|
text
|
TEXT_KEY
|
String
|
The text displayed for the node. |
icon
|
ICON_KEY
|
String
|
The URI of the icon to display for the node. The image file should be 16 pixels wide and 22 pixels high. The defaults are that no icon is rendered for leaf nodes, and a default folder icon is rendered for nodes with children. |
selected
|
SELECTED_KEY
|
Boolean
|
This is set to true for nodes to be drawn as selected. |
destination
|
DESTINATION_KEY
|
String
|
The URI to go to when the text is clicked. |
targetFrame
|
TARGET_FRAME_KEY
|
String
|
The target frame for the destination. |
expandable
|
EXPANDABLE_KEY
|
String
|
Used to determine if node is expanded, collapsed, or leaf. The possible values are:
|
collapseDestination
|
COLLAPSE_DESTINATION_KEY
|
String
|
The URI used to collapse the node when it's currently expanded. |
expandDestination
|
EXPAND_DESTINATION_KEY
|
String
|
The URI used to expand the node when it's currently collapsed. |
nodes
|
NODES_KEY
|
DataObjectList
|
The children of the node. |
So let's take a look at an example which adds some data.
<dataScope xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:data="http://xmlns.oracle.com/uix/ui"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller" >
<contents>
<!-- UIX Components -->
<form name="myForm" >
<contents>
<tree data:nodes="nodes@data:Nodes" />
</contents>
</form>
</contents>
<!-- Data -->
<provider>
<data name="data:Nodes">
<inline>
<nodes text="Shop"
destination="http://www.oracle.com"
expandable="expanded">
<nodes text="Books"
destination="http://www.oracle.com"
expandable="collapsed">
<nodes text="Sale"
destination="http://www.oracle.com"
expandable="collapsed">
<nodes text="Paperbacks"
expandable="no"
destination="http://www.oracle.com" />
</nodes>
<nodes text="Fiction"
destination="http://www.oracle.com"
expandable="no"/>
<nodes text="Nonfiction"
destination="http://www.oracle.com"
expandable="no"/>
</nodes>
<nodes text="Hardware"
expandable="collapsed">
<nodes text="Desktops"
destination="http://www.oracle.com"
expandable="no"/>
<nodes text="Notebooks"
destination="http://www.oracle.com"/>
</nodes>
</nodes>
</inline>
</data>
</provider>
</dataScope>
The example above should start to clarify the link between the key-value pairs on the
DataObject
s and how these are displayed by the tree. The value returned by the
"text"
key is displayed as the text for the node. The nodes "Shop" and "Books" are links because we have set a value for the
"destination"
key, while we have not done so for "Hardware" and so this node is therefore not clickable. The value returned by the
"expandable"
key determines whether or not the children are visible. Thus the children of "Shop" are visible while those for "Books" are not.
You might wonder why nothing happens when you click on the plus and minus icons. The
TreeBean
itself only renders the tree as indicated by the key-value pairs on each
DataObject
, it does not handle making the tree interactive. Remember, the
TreeBean
is a UIX Component; it is meant to take its children and attributes and output the appropriate content for one render, or one pass through the bean. To make it interactive we need something to handle the flow from one state to another. This can be accomplished with the UIX Controller, for example, or a servlet that you write. We have provided a class to simplify making the tree interactive, which will be discussed below. First, however, we will discuss how to influence the way the tree looks.
Now let's change a few "collapsed" values to "expanded" and look at the difference.
<dataScope xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:data="http://xmlns.oracle.com/uix/ui"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller" >
<contents>
<!-- UIX Components -->
<form name="myForm" >
<contents>
<tree data:nodes="nodes@data:Nodes" />
</contents>
</form>
</contents>
<!-- Data -->
<provider>
<data name="data:Nodes">
<inline>
<nodes text="Shop"
destination="http://www.oracle.com"
expandable="expanded">
<nodes text="Books"
destination="http://www.oracle.com"
expandable="expanded">
<nodes text="Sale"
destination="http://www.oracle.com"
expandable="expanded">
<nodes text="Paperbacks"
expandable="no"
destination="http://www.oracle.com" />
</nodes>
<nodes text="Fiction"
destination="http://www.oracle.com"
expandable="no"/>
<nodes text="Nonfiction"
destination="http://www.oracle.com"
expandable="no"/>
</nodes>
<nodes text="Hardware"
expandable="expanded">
<nodes text="Desktops"
destination="http://www.oracle.com"
expandable="no"/>
<nodes text="Notebooks"
destination="http://www.oracle.com"
expandable="no"/>
</nodes>
</nodes>
</inline>
</data>
</provider>
</dataScope>
Note that the default is expanded="no" so you can leave off this key-value pair for leaves. If you leave it off for non-leaf items or put "no" as the value then the icon indicating +/- is missing. Note that on the "Shop" node there is no expandable key and the icon is removed. Note also that the "Hardware" node has the "expandable" key set to "no" despite it's not being a leaf, and the icon is removed.
<dataScope xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:data="http://xmlns.oracle.com/uix/ui"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller" >
<contents>
<!-- UIX Components -->
<form name="myForm" >
<contents>
<tree data:nodes="nodes@data:Nodes" />
</contents>
</form>
</contents>
<!-- Data -->
<provider>
<data name="data:Nodes">
<inline>
<!-- "expandable" key removed -->
<nodes text="Shop"
destination="http://www.oracle.com" >
<nodes text="Books"
destination="http://www.oracle.com"
expandable="expanded">
<nodes text="Sale"
destination="http://www.oracle.com"
expandable="expanded">
<nodes text="Paperbacks"
destination="http://www.oracle.com" />
</nodes>
<nodes text="Fiction"
destination="http://www.oracle.com"/>
<nodes text="Nonfiction"
destination="http://www.oracle.com"/>
</nodes>
<!-- "expandable" changed to "no" -->
<nodes text="Hardware"
expandable="no" >
<nodes text="Desktops"
destination="http://www.oracle.com"/>
<nodes text="Notebooks"
destination="http://www.oracle.com"/>
</nodes>
</nodes>
</inline>
</data>
</provider>
</dataScope>
You can also set nodes to look selected by setting the value for the
"selected"
key to true. In the following example "Shop" and "Sale" are highlighted with a blue background and white text.
<dataScope xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:data="http://xmlns.oracle.com/uix/ui"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller" >
<contents>
<!-- UIX Components -->
<form name="myForm" >
<contents>
<tree data:nodes="nodes@data:Nodes" />
</contents>
</form>
</contents>
<!-- Data -->
<provider>
<data name="data:Nodes">
<inline>
<nodes text="Shop"
destination="http://www.oracle.com"
expandable="expanded"
selected="true">
<nodes text="Books"
destination="http://www.oracle.com"
expandable="expanded">
<nodes text="Sale"
destination="http://www.oracle.com"
expandable="expanded"
selected="true">
<nodes text="Paperbacks"
destination="http://www.oracle.com" />
</nodes>
<nodes text="Fiction"
destination="http://www.oracle.com"/>
<nodes text="Nonfiction"
destination="http://www.oracle.com"/>
</nodes>
<nodes text="Hardware"
expandable="expanded" >
<nodes text="Desktops"
destination="http://www.oracle.com"/>
<nodes text="Notebooks"
destination="http://www.oracle.com"/>
</nodes>
</nodes>
</inline>
</data>
</provider>
</dataScope>
By default nodes with children will render with a folder icon. You can add icons to leaves or replace the folder icon by setting the value for the
"icon"
key to the URI of another image. Let's look at an example:
<dataScope xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:data="http://xmlns.oracle.com/uix/ui"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller" >
<contents>
<!-- UIX Components -->
<form name="myForm" >
<contents>
<tree data:nodes="nodes@data:Nodes" />
</contents>
</form>
</contents>
<!-- Data -->
<provider>
<data name="data:Nodes">
<inline>
<nodes text="Shop"
destination="http://www.oracle.com"
expandable="expanded"
icon="/docs/devguide/images/data_trees/info.gif">
<nodes text="Books"
destination="http://www.oracle.com"
expandable="expanded"
icon="/docs/devguide/images/data_trees/info.gif">
<nodes text="Sale"
destination="http://www.oracle.com"
expandable="expanded"
icon="/docs/devguide/images/data_trees/info.gif">
<nodes text="Paperbacks"
destination="http://www.oracle.com" />
</nodes>
<nodes text="Fiction"
destination="http://www.oracle.com"/>
<nodes text="Nonfiction"
destination="http://www.oracle.com"/>
</nodes>
<nodes text="Hardware"
expandable="expanded"
icon="/docs/devguide/images/data_trees/info.gif">
<nodes text="Desktops"
destination="http://www.oracle.com"/>
<nodes text="Notebooks"
destination="http://www.oracle.com"/>
</nodes>
</nodes>
</inline>
</data>
</provider>
</dataScope>
By default a link is displayed for each node in the tree, but setting the
"nodeStamp"
allows you more control over what is displayed. The
"nodeStamp"
is a named child of the
TreeBean
.
What is a stamp? A stamp is just a UINode -- any UINode -- that is rendered more than once on a given page. In the tree there is one stamp and this stamp is repeated, or rendered once for each node in the tree. This means each node looks similar, but can have different data through data binding. The following example uses a node stamp. Notice that we can add arbitrary key-value pairs into the
DataObject
s in the tree and then use data binding to extract these values.
<dataScope xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:data="http://xmlns.oracle.com/uix/ui"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller" >
<contents>
<!-- UIX Components -->
<form name="myForm" >
<contents>
<tree data:nodes="nodes@data:Nodes" >
<nodeStamp>
<flowLayout>
<contents>
<checkBox data:rendered="rendered"
data:disabled="disabled"
data:checked="checked"/>
<styledText data:text="text" />
</contents>
</flowLayout>
</nodeStamp>
</tree>
</contents>
</form>
</contents>
<!-- Data -->
<provider>
<data name="data:Nodes">
<inline>
<nodes text="Shop"
destination="http://www.oracle.com"
expandable="expanded"
rendered="false"
icon="/docs/devguide/images/data_trees/info.gif">
<nodes text="Books"
destination="http://www.oracle.com"
expandable="expanded"
rendered="false"
icon="/docs/devguide/images/data_trees/info.gif">
<nodes text="Sale"
destination="http://www.oracle.com"
expandable="expanded"
rendered="false"
icon="/docs/devguide/images/data_trees/info.gif">
<nodes text="Paperbacks"
destination="http://www.oracle.com"
disabled="true" />
</nodes>
<nodes text="Fiction"
destination="http://www.oracle.com"
checked="true"/>
<nodes text="Nonfiction"
destination="http://www.oracle.com"/>
</nodes>
<nodes text="Hardware"
expandable="expanded"
rendered="false"
icon="/docs/devguide/images/data_trees/info.gif">
<nodes text="Desktops"
destination="http://www.oracle.com"/>
<nodes text="Notebooks"
destination="http://www.oracle.com"/>
</nodes>
</nodes>
</inline>
</data>
</provider>
</dataScope>
Note that the height of the nodes cannot be arbitrarily large. Currently each node can be about 45 pixels high.
Now let's make the tree interactive. When a node is in the collapsed state, the value associated with the
"expandDestination"
key is used as the destination to expand the node. When a node is in the expanded state, the value associated with the
"collapseDestination"
key is used as the destination to collapse the node. Users setting these destinations themselves must keep track of the state of the tree, in other words which nodes are expanded/collapsed and which are selected.
In order to simplify using the tree, we have provided the class
ClientStateTreeDataProxy
. This proxy keeps track of the expanded/collapsed state of the tree by setting up the expand and collapse destinations. The proxy to be used is set using the
"proxy"
attribute.
When using the
ClientStateTreeDataProxy
the state of the tree is kept on the client. The proxy sets the expand/collapse destinations such that the following name-value pairs are sent to the server when a node is expanded or collapsed.
The value of 'event' is the name of a UIX Controller event. Note that the value is 'expand' whether or not a node is being expanded or collapsed.
The value of 'source' is the value set for the
ID
attribute. We have not yet used the
ID
attribute but we will include it in the next example.
The values associated with 'node', 'state', and 'selection' are used at the server to create a new proxy. Users do not need to worry about what these values actually are, they must only ask for the values and pass these values to the
ClientStateTreeDataProxy
constructor. The example below should clarify this process.
If you set up the tree with the proper state and then use the
ClientStateTreeDataProxy
constructor that takes null for the tree state, the proxy will attempt to pull the expanded/collapsed state from the node data. So, to have a tree opened up to a specific node by default, you need only set the nodes along the path to the root to be expanded. Again, the following examples should clarify how to use
ClientStateTreeDataProxy
.
This example requires Java code. The Java follows the UIX example.
<ctrl:page xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller"
xmlns:data="http://xmlns.oracle.com/uix/ui">
<ctrl:content xmlns:ui="http://xmlns.oracle.com/uix/ui">
<body>
<contents>
<dataScope xmlns="http://xmlns.oracle.com/uix/ui">
<contents>
<!-- UIX Components -->
<form name="myForm" >
<contents>
<tree id="tree"
formSubmitted="true"
data:nodes="nodes@data:Nodes"
data:proxy="proxy@TreeProxy"/>
</contents>
</form>
</contents>
<!-- Data -->
<provider>
<data name="TreeProxy">
<method class="oracle.cabo.doc.demo.DataTrees"
method="getTreeProxy"/>
</data>
<data name="data:Nodes">
<inline>
<nodes text="Shop"
destination="http://www.oracle.com"
expandable="expanded">
<nodes text="Books"
destination="http://www.oracle.com"
expandable="expanded">
<nodes text="Sale"
destination="http://www.oracle.com"
expandable="expanded">
<nodes text="Paperbacks"
destination="http://www.oracle.com" />
</nodes>
<nodes text="Fiction"
destination="http://www.oracle.com"/>
<nodes text="Nonfiction"
destination="http://www.oracle.com"/>
</nodes>
<nodes text="Hardware"
expandable="expanded">
<nodes text="Desktops"
destination="http://www.oracle.com"/>
<nodes text="Notebooks"
destination="http://www.oracle.com"/>
</nodes>
</nodes>
</inline>
</data>
</provider>
</dataScope>
</contents>
</body>
</ctrl:content>
<!-- UIX Controller -->
<ctrl:handlers xmlns="http://xmlns.oracle.com/uix/controller">
<event name="expand">
<method class="oracle.cabo.doc.demo.DataTrees"
method="expandEventHandler"/>
</event>
</ctrl:handlers>
</ctrl:page>
The following are the two methods in bold above. All subsequent
TreeBean
examples use these same methods. The first method is the
getTreeProxy
method which returns a
DataObject
that returns a proxy.
public static DataObject getTreeProxy(
RenderingContext rc,
String ns,
String name)
{
return new TreeProxyDataObject(null);
}
private static class TreeProxyDataObject implements DataObject
{
public TreeProxyDataObject(
String submitURL
)
{
_submitURL = submitURL;
}
public Object selectValue(
RenderingContext rc,
Object key
)
{
BajaContext bc = BajaRenderingContext.getBajaContext(rc);
EventResult result = EventResult.getEventResult(bc);
Object proxy = (result==null) ? null : result.getProperty("proxy");
if (proxy==null)
proxy = new ClientStateTreeDataProxy( _submitURL, null, null, null);
return proxy;
}
String _submitURL;
}
The
expandEventHandler
method handles 'expand' events when they are generated. Note in
expandEventHandler
you just pass the values associated with the parameters 'state'
(UIConstants.STATE_PARAM)
, 'node'
(UIConstants.NODE_PARAM)
, and 'selection'
(UIConstants.SELECTION_PARAM)
to a new
ClientStateTreeProxy
. This new proxy is set as a property on the context and is subsequently returned by the
TreeProxyDataObject
created in the method above.
public static EventResult expandEventHandler(
BajaContext context,
Page page,
PageEvent event )throws Throwable
{
String state = event.getParameter(UIConstants.STATE_PARAM);
String node = event.getParameter(UIConstants.NODE_PARAM);
String selection = event.getParameter(UIConstants.SELECTION_PARAM);
EventResult result = new EventResult(page);
Object proxy = new ClientStateTreeDataProxy(null, state, node, selection);
result.setProperty("proxy", proxy);
return result;
}
Selection will be handled by the proxy if the constructor that takes a selection parameter is used. Currently the proxy only supports single selection. A node will remain selected (until a new selection is made) even if it is not visible. The selection state is handled through an onClick handler. Let's look at an example that uses a node stamp. Notice that the background and text change color as a node's check box is clicked.
SELECTION IS NOT AVAILABLE ON NETSCAPE WHEN USING A PROXY.
<ctrl:page xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller"
xmlns:data="http://xmlns.oracle.com/uix/ui"
xmlns:html="http://www.w3.org/TR/REC-html40">
<ctrl:content xmlns:ui="http://xmlns.oracle.com/uix/ui">
<body>
<contents>
<dataScope xmlns="http://xmlns.oracle.com/uix/ui">
<contents>
<!-- UIX Components -->
<form name="myForm" >
<contents>
<tree id="tree"
formSubmitted="true"
data:nodes="nodes@data:Nodes"
data:proxy="proxy@TreeProxy">
<nodeStamp>
<flowLayout>
<contents>
<checkBox data:rendered="rendered"
data:disabled="disabled"
data:checked="checked"/>
<link data:destination="destination" data:text="text" />
</contents>
</flowLayout>
</nodeStamp>
</tree>
</contents>
</form>
</contents>
<!-- Data -->
<provider>
<data name="TreeProxy">
<method class="oracle.cabo.doc.demo.DataTrees"
method="getTreeProxy"/>
</data>
<data name="data:Nodes">
<inline>
<nodes text="Shop"
destination="http://www.oracle.com"
expandable="expanded"
rendered="false"
icon="/docs/devguide/images/data_trees/info.gif" >
<nodes text="Books"
destination="http://www.oracle.com"
expandable="expanded"
rendered="false"
icon="/docs/devguide/images/data_trees/info.gif" >
<nodes text="Sale"
destination="http://www.oracle.com"
expandable="expanded"
rendered="false"
icon="/docs/devguide/images/data_trees/info.gif">
<nodes text="Paperbacks"
destination="http://www.oracle.com"
disabled="true" />
</nodes>
<nodes text="Fiction"
destination="http://www.oracle.com"
checked="true"/>
<nodes text="Nonfiction"
destination="http://www.oracle.com"/>
</nodes>
<nodes text="Hardware"
expandable="expanded"
rendered="false"
icon="/docs/devguide/images/data_trees/info.gif" >
<nodes text="Desktops"
destination="http://www.oracle.com"/>
<nodes text="Notebooks"
destination="http://www.oracle.com"/>
</nodes>
</nodes>
</inline>
</data>
</provider>
</dataScope>
</contents>
</body>
</ctrl:content>
<!-- UIX Controller -->
<ctrl:handlers xmlns="http://xmlns.oracle.com/uix/controller">
<event name="expand">
<method class="oracle.cabo.doc.demo.DataTrees"
method="expandEventHandler"/>
</event>
</ctrl:handlers>
</ctrl:page>
Many developers will want to use frames with the
TreeBean
. The following is an example of how to do just that. This involves three uiXML files. The first sets up the frames.
<page xmlns="http://xmlns.oracle.com/uix/controller"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller"
xmlns:data="http://xmlns.oracle.com/uix/ui"
xmlns:http="http://www.w3.org/TR/REC-html40">
<content>
<frameBorderLayout xmlns="http://xmlns.oracle.com/uix/ui" >
<left>
<frame source="B-2-11.uix" name="tree" width="30%" />
</left>
<center>
<frame source="B-2-12.uix" name="contents" />
</center>
</frameBorderLayout>
</content>
</page>
The next is the tree itself with some new destinations and the addition of the
"targetFrame"
key which specifies the frame to direct the content to.
<ctrl:page xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller"
xmlns:data="http://xmlns.oracle.com/uix/ui"
xmlns:html="http://www.w3.org/TR/REC-html40">
<ctrl:content xmlns:ui="http://xmlns.oracle.com/uix/ui">
<body>
<contents>
<dataScope xmlns="http://xmlns.oracle.com/uix/ui">
<contents>
<!-- UIX Components -->
<form name="myForm" >
<contents>
<tree id="tree"
formSubmitted="true"
data:nodes="nodes@data:Nodes"
data:proxy="proxy@TreeProxy"/>
</contents>
</form>
</contents>
<provider>
<!-- Data -->
<data name="TreeProxy">
<method class="oracle.cabo.doc.demo.DataTrees"
method="getTreeProxy"/>
</data>
<data name="data:Nodes">
<inline>
<nodes text="Shop"
destination="http://otn.oracle.com/index.html"
targetFrame="contents"
expandable="expanded">
<nodes text="Books"
destination="http://www.osborne.com/oracle/"
targetFrame="contents"
expandable="expanded">
<nodes text="Sale"
destination="http://www.oracle.com"
targetFrame="contents"
expandable="expanded">
<nodes text="Paperbacks"
destination="http://www.oracle.com/appsnet/"
targetFrame="contents"/>
</nodes>
<nodes text="Fiction"
destination="http://www.oracle.com/oramag/"
targetFrame="contents"/>
<nodes text="Nonfiction"
destination="http://otn.oracle.com/support/content.html"
targetFrame="contents"/>
</nodes>
<nodes text="Hardware"
expandable="expanded" >
<nodes text="Desktops"
destination="http://otn.oracle.com/training/content.html"
targetFrame="contents"/>
<nodes text="Notebooks"
destination="http://otn.oracle.com/tech/content.html"
targetFrame="contents"/>
</nodes>
</nodes>
</inline>
</data>
</provider>
</dataScope>
</contents>
</body>
</ctrl:content>
<!-- UIX Controller -->
<ctrl:handlers xmlns="http://xmlns.oracle.com/uix/controller">
<event name="expand">
<method class="oracle.cabo.doc.demo.DataTrees"
method="expandEventHandler"/>
</event>
</ctrl:handlers>
</ctrl:page>
The last is an empty uiXML file.
<ctrl:page xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller"
xmlns:data="http://xmlns.oracle.com/uix/ui"
xmlns:html="http://www.w3.org/TR/REC-html40"/>
The
BrowseMenuBean
provides another way to view hierarchical or tree-structured data.
The
BrowseMenuBean
supports four named children:
uiXML Key | UIConstant | Description |
---|---|---|
location
|
LOCATION_CHILD
|
Bean to use to render locator element on top of BrowseMenu. |
contentLink
|
CONTENT_LINK_CHILD
|
Bean to use to link to content of the current category. |
categories
|
CATEGORIES_CHILD
|
Bean to use to render categories. |
items
|
ITEMS_CHILD
|
Bean to use to render items. |
The
BrowseMenuBean
is essentially a layout manager which places these named children at the appropriate locations. The Browser Look and Feel (BLAF) guidelines show a
BreadCrumbsBean
as the location element, a
LinkBean
as the content link element, a
BulletedListBean
to display categories and another
BulletedListBean
to display items.
The
BrowseMenuBean
supports the following attributes:
uiXML Key | UIConstant | Type | Description |
---|---|---|---|
id
|
ID_ATTR
|
String
|
A page-wide unique id. |
title
|
TITLE_ATTR
|
String
|
Sets the title of the BrowseMenu. This text will be displayed in the top header. If this attribute is not set the default text will be set to "Browse". |
categoryTitle
|
CATEGORY_TITLE_ATTR
|
String
|
Sets the title of the category section. This text will be displayed in the header above the categories. If this attribute is not set the default text will be set to "Categories". |
itemTitle
|
ITEM_TITLE_ATTR
|
String
|
Sets the title of the items section. This text will be displayed in the header above the items. If this attribute is not set the default text will be set to "Items". |
longDesc
|
LONG_DESC_ATTR
|
String
|
Sets the description seen just under the title to describe the current location. If this attribute is not set the default text will be set to the empty string. |
formName
|
FORM_NAME_ATTR
|
String
|
The name of the form to submit. |
formSubmitted
|
FORM_SUBMITTED_ATTR
|
Boolean
|
Whether or not to use form submission. Form submission is not the default, thus if form submission is desired then this attribute must be explicitly set to true. |
Let's look at an image which may clarify how these named children and attributes are used.
We will start our
BrowseMenuBean
discussion with some simple examples in which everything is hardcoded. We will provide more complicated examples below.
Note that the "View Source" link has been moved to the bottom of the page.
<browseMenu/>
This is the simplest example and all we see is a header with the text "Browse". In order to make this more interesting we need to add data.
First we will add a
"categories"
child. The categories correspond to tree nodes that have children, or are not leaves.
<browseMenu>
<categories>
<bulletedList rows="10">
<contents>
<link text="Sale" destination="http://www.oracle.com"/>
</contents>
</bulletedList>
</categories>
</browseMenu>
You might remember that in the introduction we said that when you click on the "Sale" link we should see a similar page with the breadcrumbs "Shop> Books> Sale" at the top and the link "Paperbacks" under the "Items" header, however the sale link in the above example doesn't do this. Like the
TreeBean
, the
BrowseMenuBean
itself does not handle interactivity. Remember, the
BrowseMenuBean
is a UIX Component; it is meant to take its children and attributes and output the appropriate content for one render, or one pass through the bean. To make it interactive we need something to handle the flow from one state to another. This can be accomplished with the UIX Controller, for example, or a servlet that you write. We have provided a class to simplify making the
BrowseMenuBean
interactive, which will be discussed below. First, however, we will discuss how to influence the way the
BrowseMenuBean
looks.
To continue we will add an
"items"
child. The items correspond to leaves in the tree.
<browseMenu>
<categories>
<bulletedList rows="10">
<contents>
<link text="Sale" destination="http://www.oracle.com"/>
</contents>
</bulletedList>
</categories>
<items>
<bulletedList rows="10">
<contents>
<link text="Fiction" destination="http://www.oracle.com"/>
<link text="Nonfiction" destination="http://www.oracle.com"/>
</contents>
</bulletedList>
</items>
</browseMenu>
Now we will add a
"location"
child. This helps orient the user as to their location in the tree.
<browseMenu>
<location>
<breadCrumbs>
<contents>
<link text="Shop" destination="http://www.oracle.com"/>
<link text="Books" destination="http://www.oracle.com"/>
</contents>
</breadCrumbs>
</location>
<categories>
<bulletedList rows="10">
<contents>
<link text="Sale" destination="http://www.oracle.com"/>
</contents>
</bulletedList>
</categories>
<items>
<bulletedList rows="10">
<contents>
<link text="Fiction" destination="http://www.oracle.com"/>
<link text="Nonfiction" destination="http://www.oracle.com"/>
</contents>
</bulletedList>
</items>
</browseMenu>
Finally we will add a
"contentLink"
child. The content link allows the user to access additional information/instructions.
<browseMenu>
<location>
<breadCrumbs>
<contents>
<link text="Shop" destination="http://www.oracle.com"/>
<link text="Books" destination="http://www.oracle.com"/>
</contents>
</breadCrumbs>
</location>
<contentLink>
<link text="More Information" destination="http://www.oracle.com"/>
</contentLink>
<categories>
<bulletedList rows="10">
<contents>
<link text="Sale" destination="http://www.oracle.com"/>
</contents>
</bulletedList>
</categories>
<items>
<bulletedList rows="10">
<contents>
<link text="Fiction" destination="http://www.oracle.com"/>
<link text="Nonfiction" destination="http://www.oracle.com"/>
</contents>
</bulletedList>
</items>
</browseMenu>
Now that we've seen the basic layout of the BrowseMenu let's take a look at the attributes.
First we will set the
"title"
attribute, changing the title from "Browse" to "Books".
<browseMenu title="Books">
<location>
<breadCrumbs>
<contents>
<link text="Shop" destination="http://www.oracle.com"/>
<link text="Books" destination="http://www.oracle.com"/>
</contents>
</breadCrumbs>
</location>
<contentLink>
<link text="More Information" destination="http://www.oracle.com"/>
</contentLink>
<categories>
<bulletedList rows="10">
<contents>
<link text="Sale" destination="http://www.oracle.com"/>
</contents>
</bulletedList>
</categories>
<items>
<bulletedList rows="10">
<contents>
<link text="Fiction" destination="http://www.oracle.com"/>
<link text="Nonfiction" destination="http://www.oracle.com"/>
</contents>
</bulletedList>
</items>
</browseMenu>
Next we will set the
"categoryTitle"
attribute, changing the text from "Categories" to "Have Subcategories". We will also set the
"itemTitle"
attribute, changing the text from "Items" to "No Subcategories".
<browseMenu title="Books"
categoryTitle="Have Subcategories"
itemTitle="No Subcategories">
<location>
<breadCrumbs>
<contents>
<link text="Shop" destination="http://www.oracle.com"/>
<link text="Books" destination="http://www.oracle.com"/>
</contents>
</breadCrumbs>
</location>
<contentLink>
<link text="More Information" destination="http://www.oracle.com"/>
</contentLink>
<categories>
<bulletedList rows="10">
<contents>
<link text="Sale" destination="http://www.oracle.com"/>
</contents>
</bulletedList>
</categories>
<items>
<bulletedList rows="10">
<contents>
<link text="Fiction" destination="http://www.oracle.com"/>
<link text="Nonfiction" destination="http://www.oracle.com"/>
</contents>
</bulletedList>
</items>
</browseMenu>
Now we will set the
"longDesc"
attribute. This area can be used for descriptions or instructions.
<browseMenu title="Books"
categoryTitle="Have Subcategories"
itemTitle="No Subcategories"
longDesc="Instructions: buy, buy, buy!">
<location>
<breadCrumbs>
<contents>
<link text="Shop" destination="http://www.oracle.com"/>
<link text="Books" destination="http://www.oracle.com"/>
</contents>
</breadCrumbs>
</location>
<contentLink>
<link text="More Information" destination="http://www.oracle.com"/>
</contentLink>
<categories>
<bulletedList rows="10">
<contents>
<link text="Sale" destination="http://www.oracle.com"/>
</contents>
</bulletedList>
</categories>
<items>
<bulletedList rows="10">
<contents>
<link text="Fiction" destination="http://www.oracle.com"/>
<link text="Nonfiction" destination="http://www.oracle.com"/>
</contents>
</bulletedList>
</items>
</browseMenu>
There are two final attributes,
"formSubmitted"
and
"formName"
. We will see the application of these attributes below.
So what happened to our tree-structured data? The next section discusses how to use a
BrowseMenuBean
with tree-structured data.
While the purpose of the
BrowseMenuBean
is to allow users to browse through complex sets of hierarchical objects, the hierarchical data isn't added to the BrowseMenu itself as it is for the
TreeBean
. In fact the
BrowseMenuBean
expects the named children and attributes to contain all the appropriate data. We provide a class to make binding this data relatively simple.
BrowseNodeDataObject
takes a tree of
DataObject
s and a current location and extracts the appropriate information. The "tree of
DataObject
s" part should sound familiar. In fact an instance of
BrowseNodeDataObject
can take the same tree of data used by a
TreeBean
.
BrowseNodeDataObject
expects the nodes in the tree to use the following keys.
uiXML Key | UIConstant | Type | Description |
---|---|---|---|
text
|
TEXT_KEY
|
String
|
The text for the node. |
destination
|
DESTINATION_KEY
|
String
|
The URI for a link. |
destinationText
|
DESTINATION_TEXT_KEY
|
String
|
The text for a link. |
description
|
DESCRIPTION_KEY
|
String
|
A description or instruction. |
nodes
|
NODES_KEY
|
DataObjectList
|
The children of the node. |
Again, this should be familiar. There are two new keys. The value associated with the
"description"
key is intended to map to the value of the
"longDesc"
attribute.
The value of the
"destinationText"
key is intended to be the text for the
"contentLink"
child. In the
TreeBean
if a node has a destination it can be displayed as a link, even if the node has children. For example looking at a tree example if we click on the text "Books" it is a link. In the BrowseMenu the text "Books" is in a header, and is not clickable. Thus a separate link is needed and this is the purpose of the
"contentLink"
child.
Let's look at an updated version of the tree of data that uses these keys. Notice that it is fine for a
DataObject
to include key-value pairs not needed by the BrowseMenu. For example, the BrowseMenu will ignore the
"expanded"
key and its associated value.
<nodes text="Shop"
destination="http://www.oracle.com"
destinationText="More Information"
expandable="expanded">
<nodes text="Books"
destination="http://www.oracle.com"
destinationText="More Information"
description="Instructions: buy, buy, buy!"
expandable="expanded">
<nodes text="Sale"
destination="http://www.oracle.com"
expandable="expanded">
<nodes text="Paperbacks"
destination="http://www.oracle.com"/>
</nodes>
<nodes text="Fiction"
destination="http://www.oracle.com"/>
<nodes text="Nonfiction"
destination="http://www.oracle.com"/>
</nodes>
<nodes text="Hardware"
expandable="expanded" >
<nodes text="Desktops"
destination="http://www.oracle.com"/>
<nodes text="Notebooks"
destination="http://www.oracle.com"/>
</nodes>
</nodes>
The
BrowseNodeDataObject
constructor takes a tree of data and the current location in the tree. From that it creates values for the following keys:
uiXML Key | Java Key | Type | Description |
---|---|---|---|
locationData
|
BrowseNodeDataObject.
|
DataObjectList
|
The nodes for the location child. These are all the nodes along the path from the root of the tree to the current location node. |
categoriesData
|
BrowseNodeDataObject.
|
DataObjectList
|
The nodes for the categories child. These are all the children of the current location data object who have children themselves, or are not leaves in the tree. |
itemsData
|
BrowseNodeDataObject.
|
DataObjectList
|
The nodes for the items child. These are all the children of the current location data object who do not have children themselves, or are leaves in the tree. |
renderLocation
|
BrowseNodeDataObject.
|
Boolean
|
If the current location node is one of the 'roots' of the tree, this is set to
|
renderContentLink
|
BrowseNodeDataObject.
|
Boolean
|
If the current location node is queried with
|
renderCategories
|
BrowseNodeDataObject.
|
Boolean
|
If the current location node has no children who are categories, or internal nodes in the tree, this is set to
|
renderItems
|
BrowseNodeDataObject.
|
Boolean
|
If the current location node has no children who are items, or tree leaves, this is set to
|
currentState
|
BrowseNodeDataObject.
|
String
|
The current state of the browse menu as passed in to the constructor. |
So let's say the current location being viewed by the user is the "Books" node in the tree above. The keys above would return the following.
BrowseNodeDataObject.LOCATION_DATA_KEY
- A
DataObjectList
containing the nodes "Shop" and "Books".BrowseNodeDataObject.CATEGORIES_DATA_KEY
- A
DataObjectList
containing "Sale".BrowseNodeDataObject.ITEMS_DATA_KEY
- A
DataObjectList
containing the nodes "Fiction" and "Nonfiction".BrowseNodeDataObject.RENDER_LOCATION_KEY
- set to
Boolean.TRUE
since "Books" is not one of the 'roots' - it is a child of "Shop".BrowseNodeDataObject.RENDER_CONTENT_LINK_KEY
- set to
Boolean.TRUE
since the "Books" node returns "more information" when queried with
UIConstants.DESTINATION_TEXT_KEY
.BrowseNodeDataObject.RENDER_CATEGORIES_KEY
- Set to
Boolean.TRUE
since there are categories.BrowseNodeDataObject.RENDER_ITEMS_KEY
- Set to
Boolean.TRUE
since there are items.BrowseNodeDataObject.CURRENT_STATE_KEY
- A String containing the state indicating we are looking at the "Books" node. In this case this string would be "0,0" since "Shop" is roots[0] and "Books" is its 0th child.The "location" and "categories" data are
DataObjectList
s where the destination on each
DataObject
is retrieved with the key
UIConstant.DESTINATION_KEY
. The destination is set such that if a location or categories element is pressed the following name-value pairs are returned to the server:
The value of 'event' is the name of a UIX Controller event, which is 'browse' in this case.
The value of 'source' is the value set for the
ID
attribute. We have not yet used the
ID
attribute but we will include it in the next example.
The value associated with 'location' is used at the server to create a new
BrowseNodeDataObject
. Users do not need to worry about what this value is, they must only ask for the value and pass this value to the
BrowseNodeDataObject
constructor. The example below should clarify this process.
Users do have to set up an initial
BrowseNodeDataObject
, passing in a
String
representing the the location of the node in the tree to view, also referred to as the current location. This
String
is a comma-separated string of zero-based indices. For example if the node requested is the 4th child of the first root node then the value would be "0,3". That's 0 for the first root node and 3 for its fourth child. The location for the 7th child of the 4th child of the first root node would be stored as "0,3,6" and so on. The value of the current location in the initial
BrowseNodeDataObject
would normally be something like "0" or "1", indicating one of the roots of the tree of data. The default is "0" if the current location passed in is null. Again, the example below should clarify.
Let's look at an example of how to use
BrowseNodeDataObject
. The Java code required by this example follows uiXML example. In this example we have bound the value of the
"title"
attribute and the data for the
"categories"
child.
<ctrl:page xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller"
xmlns:data="http://xmlns.oracle.com/uix/ui" >
<ctrl:content xmlns:ui="http://xmlns.oracle.com/uix/ui">
<body>
<contents>
<dataScope xmlns="http://xmlns.oracle.com/uix/ui">
<contents>
<!-- UIX Components -->
<browseMenu id="myBrowseMenu"
data:title="text@data:browseData">
<categories>
<bulletedList>
<contents data:childData="categoriesData@data:browseData">
<link data:text="text" data:destination="destination"/>
</contents>
</bulletedList>
</categories>
</browseMenu>
</contents>
<!-- Data -->
<provider>
<data name="data:browseData">
<method class="oracle.cabo.doc.demo.DataTrees" method="getBrowseNodeDataObject"/>
</data>
<data name="data:Nodes">
<inline>
<nodes text="Shop"
destination="http://www.oracle.com"
destinationText="More Information"
expandable="expanded">
<nodes text="Books"
destination="http://www.oracle.com"
destinationText="More Information"
description="Instructions: buy, buy, buy!"
expandable="expanded">
<nodes text="Sale"
destination="http://www.oracle.com"
expandable="expanded">
<nodes text="Paperbacks"
destination="http://www.oracle.com"/>
</nodes>
<nodes text="Fiction"
destination="http://www.oracle.com"/>
<nodes text="Nonfiction"
destination="http://www.oracle.com"/>
</nodes>
<nodes text="Hardware"
expandable="expanded" >
<nodes text="Desktops"
destination="http://www.oracle.com"/>
<nodes text="Notebooks"
destination="http://www.oracle.com"/>
</nodes>
</nodes>
</inline>
</data>
</provider>
</dataScope>
</contents>
</body>
</ctrl:content>
<!-- UIX Controller -->
<ctrl:handlers xmlns="http://xmlns.oracle.com/uix/controller">
<event name="browse">
<method class="oracle.cabo.doc.demo.DataTrees" method="browseEventHandler"/>
</event>
</ctrl:handlers>
</ctrl:page>
The following are the java methods that make this interactive. All subsequent
BrowseMenuBean
examples use these same methods. The first method creates an instance of
BrowseNodeDataObject
, passing in the inline tree of data and the location. The first time this method is called the value of
location
is null and the default used is "0", indicating the first root which is the "Shop" node.
public static DataObject getBrowseNodeDataObject(
RenderingContext context,
String namespace,
String name
)
{
BajaContext bajaContext = BajaRenderingContext.getBajaContext(context);
// get location property stored on context
String location = (String) bajaContext.getProperty("browse", "location");
// get inline data object
DataObject data =
context.getDataObject(UIConstants.MARLIN_NAMESPACE , "Nodes");
if ( data == null )
return null;
// get tree roots
DataObjectList tree = (DataObjectList) data.selectValue(context, "nodes");
// if location is null the default of "0" is used
return new BrowseNodeDataObject( tree, location);
}
The next method is called when there is a 'browse' event. The value of the 'location'
(UIConstants.LOCATION_PARAM)
parameter is set as a property on the context. This property is subsequently passed to the
BrowseNodeDataObject
constructor in the method above.
public static EventResult browseEventHandler(
BajaContext context,
Page page,
PageEvent event ) throws Throwable
{
String location = event.getParameter( UIConstants.LOCATION_PARAM );
// set value on context as property
context.setProperty( "browse", "location", location);
EventResult result = new EventResult( page );
return result;
}
Next we will look at a more complete example of how to use the rest of the keys of
BrowseNodeDataObject
. A little later we will show you a utility class that simplifies wiring up a
BrowseMenuBean
to a
BrowseNodeDataObject
.
<ctrl:page xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller"
xmlns:data="http://xmlns.oracle.com/uix/ui" >
<ctrl:content xmlns:ui="http://xmlns.oracle.com/uix/ui">
<body>
<contents>
<dataScope xmlns="http://xmlns.oracle.com/uix/ui">
<contents>
<!-- UIX Components -->
<browseMenu id="myBrowseMenu"
data:title="text@data:browseData"
data:longDesc="description@data:browseData">
<location>
<breadCrumbs data:rendered="renderLocation@data:browseData">
<contents data:childData="locationData@data:browseData">
<link data:text="text" data:destination="destination"/>
</contents>
</breadCrumbs>
</location>
<contentLink>
<link data:text="destinationText@data:browseData"
data:destination="destination@data:browseData"
data:rendered="renderContentLink@data:browseData"/>
</contentLink>
<categories>
<bulletedList data:rendered="renderCategories@data:browseData">
<contents data:childData="categoriesData@data:browseData">
<link data:text="text" data:destination="destination"/>
</contents>
</bulletedList>
</categories>
<items>
<bulletedList data:rendered="renderItems@data:browseData">
<contents data:childData="itemsData@data:browseData">
<link data:text="text" data:destination="destination"/>
</contents>
</bulletedList>
</items>
</browseMenu>
</contents>
<!-- Data -->
<provider>
<data name="data:browseData">
<method class="oracle.cabo.doc.demo.DataTrees" method="getBrowseNodeDataObject"/>
</data>
<data name="data:Nodes">
<inline>
<nodes text="Shop"
destination="http://www.oracle.com"
destinationText="More Information"
expandable="expanded">
<nodes text="Books"
destination="http://www.oracle.com"
destinationText="More Information"
description="Instructions: buy, buy, buy!"
expandable="expanded">
<nodes text="Sale"
destination="http://www.oracle.com"
expandable="expanded">
<nodes text="Paperbacks"
destination="http://www.oracle.com"/>
</nodes>
<nodes text="Fiction"
destination="http://www.oracle.com"/>
<nodes text="Nonfiction"
destination="http://www.oracle.com"/>
</nodes>
<nodes text="Hardware"
expandable="expanded" >
<nodes text="Desktops"
destination="http://www.oracle.com"/>
<nodes text="Notebooks"
destination="http://www.oracle.com"/>
</nodes>
</nodes>
</inline>
</data>
</provider>
</dataScope>
</contents>
</body>
</ctrl:content>
<!-- UIX Controller -->
<ctrl:handlers xmlns="http://xmlns.oracle.com/uix/controller">
<event name="browse">
<method class="oracle.cabo.doc.demo.DataTrees" method="browseEventHandler"/>
</event>
</ctrl:handlers>
</ctrl:page>
While running the examples you can see the destination for a link in the status bar by resting your mouse over the link. In the previous examples the destinations for the links in the breadcrumbs and under the "Categories" header contain the destination passed in to the
BrowseNodeDataObject
constructor, to which the name-value pairs described above are appended. If the destination is not specified then it defaults to the value returned by the call context.getURLEncoder().getDefaultURL(). An example of this default destination can be seen in the
getBrowseNodeDataObject
method described above where we use a constructor which takes no destination.
When the BrowseMenu's
"formSubmitted"
attribute is set to true, then the name-value pairs are returned through form submission. In the following example we will use form submission. Notice that now the destinations for the links in the breadcrumbs and under the "Categories" header call a function to submit the form.
<ctrl:page xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller"
xmlns:data="http://xmlns.oracle.com/uix/ui" >
<ctrl:content xmlns:ui="http://xmlns.oracle.com/uix/ui">
<body>
<contents>
<dataScope xmlns="http://xmlns.oracle.com/uix/ui">
<contents>
<!-- UIX Components -->
<form name="myForm">
<contents>
<browseMenu id="myBrowseMenu"
data:title="text@data:browseData"
data:longDesc="description@data:browseData"
formSubmitted="true"
formName="myForm">
<location>
<breadCrumbs data:rendered="renderLocation@data:browseData">
<contents data:childData="locationData@data:browseData">
<link data:text="text" data:destination="destination"/>
</contents>
</breadCrumbs>
</location>
<contentLink>
<link data:text="destinationText@data:browseData"
data:destination="destination@data:browseData"
data:rendered="renderContentLink@data:browseData"/>
</contentLink>
<categories>
<bulletedList data:rendered="renderCategories@data:browseData">
<contents data:childData="categoriesData@data:browseData">
<link data:text="text" data:destination="destination"/>
</contents>
</bulletedList>
</categories>
<items>
<bulletedList data:rendered="renderItems@data:browseData">
<contents data:childData="itemsData@data:browseData">
<link data:text="text" data:destination="destination"/>
</contents>
</bulletedList>
</items>
</browseMenu>
</contents>
</form>
</contents>
<!-- Data -->
<provider>
<data name="data:browseData">
<method class="oracle.cabo.doc.demo.DataTrees" method="getBrowseNodeDataObject"/>
</data>
<data name="data:Nodes">
<inline>
<nodes text="Shop"
destination="http://www.oracle.com"
destinationText="More Information"
expandable="expanded">
<nodes text="Books"
destination="http://www.oracle.com"
destinationText="More Information"
description="Instructions: buy, buy, buy!"
expandable="expanded">
<nodes text="Sale"
destination="http://www.oracle.com"
expandable="expanded">
<nodes text="Paperbacks"
destination="http://www.oracle.com"/>
</nodes>
<nodes text="Fiction"
destination="http://www.oracle.com"/>
<nodes text="Nonfiction"
destination="http://www.oracle.com"/>
</nodes>
<nodes text="Hardware"
expandable="expanded" >
<nodes text="Desktops"
destination="http://www.oracle.com"/>
<nodes text="Notebooks"
destination="http://www.oracle.com"/>
</nodes>
</nodes>
</nodes>
</inline>
</data>
</provider>
</dataScope>
</contents>
</body>
</ctrl:content>
<!-- UIX Controller -->
<ctrl:handlers xmlns="http://xmlns.oracle.com/uix/controller">
<event name="browse">
<method class="oracle.cabo.doc.demo.DataTrees" method="browseEventHandler"/>
</event>
</ctrl:handlers>
</ctrl:page>
BrowseMenuUtils
is a utility class which contains a method to set up a
BrowseMenuBean
to comply to BLAF, with breadCrumbs for the location element, a bulletedList of links for the items, and so on. In Java you might write code looking something like:
// create the browse menu
BrowseMenuBean browseMenu = new BrowseMenuBean();
// give it page-wide unique id - sent as value of 'source' parameter
browseMenu.setID("myBrowseMenu");
// not using form submission
browseMenu.setFormSubmitted(false);
// add the "default" containers
BrowseMenuUtils.configureBrowseMenu(NAMESPACEURI, LOCALNAME, browseMenu);
Where
NAMESPACEURI
and
LOCALNAME
are the namespace and name of the
DataObject
, most likely the
BrowseNodeDataObject
, to which the data is bound.
In uiXML this functionality can be accessed with two additional attributes. The
"defaultContents"
attribute indicates that the BrowseMenu should use the BLAF defaults, in other words
BrowseMenuUtils.configureBrowseMenu
should be called. The
"source"
attribute is the colon-separated namespace and name to pass to the
BrowseMenuUtils.configureBrowseMenu
method.
<ctrl:page xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller"
xmlns:data="http://xmlns.oracle.com/uix/ui">
<ctrl:content xmlns:ui="http://xmlns.oracle.com/uix/ui">
<body>
<contents>
<dataScope xmlns="http://xmlns.oracle.com/uix/ui">
<contents>
<!-- UIX Components -->
<form name="myForm">
<contents>
<browseMenu id="myBrowseMenu"
formSubmitted="true"
defaultContents="true"
source="data:browseData"/>
</contents>
</form>
</contents>
<!-- Data -->
<provider>
<data name="data:browseData">
<method class="oracle.cabo.doc.demo.DataTrees" method="getBrowseNodeDataObject"/>
</data>
<data name="data:Nodes">
<inline>
<nodes text="Shop"
destination="http://www.oracle.com"
destinationText="More Information"
expandable="expanded">
<nodes text="Books"
destination="http://www.oracle.com"
destinationText="More Information"
description="Instructions: buy, buy, buy!"
expandable="expanded">
<nodes text="Sale"
destination="http://www.oracle.com"
expandable="expanded">
<nodes text="Paperbacks"
destination="http://www.oracle.com"/>
</nodes>
<nodes text="Fiction"
destination="http://www.oracle.com"/>
<nodes text="Nonfiction"
destination="http://www.oracle.com"/>
</nodes>
<nodes text="Hardware"
expandable="expanded" >
<nodes text="Desktops"
destination="http://www.oracle.com"/>
<nodes text="Notebooks"
destination="http://www.oracle.com"/>
</nodes>
</nodes>
</inline>
</data>
</provider>
</dataScope>
</contents>
</body>
</ctrl:content>
<!-- UIX Controller -->
<ctrl:handlers xmlns="http://xmlns.oracle.com/uix/controller">
<event name="browse">
<method class="oracle.cabo.doc.demo.DataTrees" method="browseEventHandler"/>
</event>
</ctrl:handlers>
</ctrl:page>
Note that you can ask for the defaults and then override just the parts that need changing. For example let's say you wanted to display items in a table and you wanted to change the text of the headers above the categories and items, but otherwise you wanted to use the defaults. You would set the
"defaultContents"
and the
"source"
attributes as shown above, but you would also set the values you wanted to change. In this case you would set the
"categoryTitle"
and
"itemTitle"
attributes and set the
"items"
child with the data appropriately bound. Let's look at an example.
<ctrl:page xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:ctrl="http://xmlns.oracle.com/uix/controller"
xmlns:data="http://xmlns.oracle.com/uix/ui">
<ctrl:content xmlns:ui="http://xmlns.oracle.com/uix/ui">
<body>
<contents>
<dataScope xmlns="http://xmlns.oracle.com/uix/ui">
<contents>
<!-- UIX Components -->
<form name="myForm">
<contents>
<browseMenu id="myBrowseMenu"
formSubmitted="true"
defaultContents="true"
source="data:browseData"
categoryTitle="Have Subcategories"
itemTitle="No Subcategories">
<items>
<table data:tableData="itemsData@data:browseData"
data:rendered="renderItems@data:browseData">
<contents>
<button data:text="text" data:destination="destination"/>
<text data:text="definition"/>
</contents>
</table>
</items>
</browseMenu>
</contents>
</form>
</contents>
<!-- Data -->
<provider>
<data name="data:browseData">
<method class="oracle.cabo.doc.demo.DataTrees" method="getBrowseNodeDataObject"/>
</data>
<data name="data:Nodes">
<inline>
<nodes text="Shop"
destination="http://www.oracle.com"
destinationText="More Information"
expandable="expanded">
<nodes text="Books"
destination="http://www.oracle.com"
destinationText="More Information"
description="Instructions: buy, buy, buy!"
expandable="expanded">
<nodes text="Sale"
destination="http://www.oracle.com"
expandable="expanded">
<nodes text="Paperbacks"
destination="http://www.oracle.com"
definition="The madness of 1000 years"/>
</nodes>
<nodes text="Fiction"
destination="http://www.oracle.com"
definition="Fiction is fantasy"/>
<nodes text="nonfiction"
destination="http://www.oracle.com"
definition="nonfiction is true"/>
</nodes>
<nodes text="Hardware"
expandable="expanded" >
<nodes text="Desktops"
destination="http://www.oracle.com"
definition="Desktops aren't portable"/>
<nodes text="Notebooks"
destination="http://www.oracle.com"
definition="NoteBooks fit in your lap"/>
</nodes>
</nodes>
</inline>
</data>
</provider>
</dataScope>
</contents>
</body>
</ctrl:content>
<!-- UIX Controller -->
<ctrl:handlers xmlns="http://xmlns.oracle.com/uix/controller">
<event name="browse">
<method class="oracle.cabo.doc.demo.DataTrees" method="browseEventHandler"/>
</event>
</ctrl:handlers>
</ctrl:page>
Notice that we added key-value pairs to the
DataObject
s that were not expected by the
BrowseNodeDataObject
and used these values in the table.
The
HGridBean
provides yet another way to view tree-structured data. This bean is the union of the
TreeBean
and the
TableBean
; it implements (and extends) the functionality of the
TreeBean
while imitating the format of a
TableBean
. In addition to the expand/collapse functionality of the
TreeBean
, the HGrid supports a zoom in/out feature which allows users to focus in on (or out of) a subtree. One important difference between the HGrid and the Tree is that although the Tree supports multiple roots, the HGrid only supports a single root. It is recommended that you read about Tables before continuing with the HGrid.
An example HGrid is presented in Figure 15-5.
The parts of the HGrid shown in Figure 15-5 are explained in the list below. The numbers of the list correspond to the numbers called out in the illustration.
In this example, the object hierarchy column (6) displays the tree nodes. Clicking the arrows (3 and 4) in this column will expand or collapse the corresponding tree node. The entire tree is rooted at the node "All Colors," but this HGrid is focused on a subtree rooted at the node "Primary Colors." Clicking on the focus icons (2) in the focus column (5) allows a user to continue zooming into subtrees. The breadcrumbs area (1) shows all the parent nodes above the current focus root; clicking on these links allows a user to zoom out to that parent level. The "Expand All" and "Collapse All" links (below the control bar) allow a user to expand or collapse all the tree nodes below the current focus root.
uiXML Key | UIConstant | Description |
---|---|---|
nodeStamp
|
NODE_STAMP_CHILD
|
This child allows the header of the object-hierarchy column to be customized. It can also be used as a stamp to customize the object-hierarchy column itself. |
columnHeaderStamp
|
COLUMN_HEADER_STAMP_CHILD
|
This is the child used to stamp out all of the user-defined column headers. |
tableSelection
|
TABLE_SELECTION_CHILD
|
This is the child used to implement selection in the HGrid. |
uiXML Key | UIConstant | Type | Description |
---|---|---|---|
id
|
ID_ATTR
|
String
|
A page-wide unique id. |
treeData
|
TREE_DATA_ATTR
|
DataObject
|
This is the root of the tree being displayed by the HGrid. |
proxy
|
PROXY_ATTR
|
HGridDataProxy
|
The proxy keeps track of expand/collapse/focus state of the HGrid by setting up the expand, collapse and focus destinations. |
columnHeaderData
|
COLUMN_HEADER_DATA_ATTR
|
DataObjectList
|
The data to use when stamping out the columnHeaderStamp child. |
formSubmitted
|
FORM_SUBMITTED_ATTR
|
Boolean
|
Whether or not to use form submission. Form submission is not the default, thus if form submission is desired then this attribute must be explicitly set to true. |
The above tables list some of the important named children and attributes of
the
HGridBean
. For an exhaustive list of these
HGrid properties see the
hGrid
UIX element
documentation (or the
HGridBean
JavaDoc).
The following is an example of a simple HGrid; it displays a tree with a root node called Primary Colors. The data provider part looks familiar; it is written just like it was for a Tree. The HGrid displayed is not interactive (just like a Tree without a proxy).
<dataScope>
<contents>
<hGrid id="hg1" data:treeData="nodes@treeData"/>
</contents>
<provider>
<data name="treeData">
<inline>
<nodes text="Primary Colors" expandable="expanded">
<nodes text="Red"/>
<nodes text="Green" expandable="expanded">
<nodes text="Light"/>
<nodes text="Dark"/>
</nodes>
<nodes text="Blue"/>
</nodes>
</inline>
</data>
</provider>
</dataScope>
The above simple example doesn't demonstrate the advantage of an HGrid over a Tree. The impressive visual effect of an HGrid is demonstrated when its Table formatting properties are used, as in the following example:
<dataScope>
<contents>
<hGrid id="hg1" data:treeData="nodes@treeData">
<tableSelection>
<multipleSelection text="Select and ...">
<contents>
<button text="Copy"/>
</contents>
</multipleSelection>
</tableSelection>
<columnHeaderData>
<col text="Red Code"/>
<col text="Green Code"/>
<col text="Blue Code"/>
</columnHeaderData>
<columnHeaderStamp>
<text data:text="text"/>
</columnHeaderStamp>
<columnFormats>
<col columnDataFormat="numberFormat"/>
<col columnDataFormat="numberFormat"/>
<col columnDataFormat="numberFormat"/>
</columnFormats>
<contents>
<text data:text="r"/>
<text data:text="g"/>
<text data:text="b"/>
</contents>
</hGrid>
</contents>
<provider>
<data name="treeData">
<inline>
<nodes text="Primary Colors" expandable="expanded">
<nodes text="Red" r="Any" g="00" b="00" />
<nodes text="Green" r="00" g="Any" b="00" expandable="expanded">
<nodes text="Light" r="00" g="FF" b="00" />
<nodes text="Dark" r="00" g="88" b="00" />
</nodes>
<nodes text="Blue" r="00" g="00" b="Any"/>
</nodes>
</inline>
</data>
</provider>
</dataScope>
In the above example, the HGrid column headers are set using
columnHeaderData
and
columnHeaderStamp
, column formats are set using
columnFormats
and selection is set using
tableSelection
and implemented by
MultipleSelectionBean
. Please refer to the Table chapter for detailed descriptions of these
formatting options. The Table chapter also describes other formatting options
such as column banding and grid customization which are also pertinent to the
HGrid; however, the HGrid does not support any of the row customizations
supported by the Table. In addition the HGrid does not support table
navigation or detail-disclosure.
The
nodeStamp
child of the HGrid allows the
object-hierarchy column to be customized. It is possible to customize both the
header and the data of this column. Note that the HGrid picks a default column
header for the object hierarchy column; this header defaults to
Name. This is easily customized by using a
ColumnBean
as the
nodeStamp
of the HGrid, as in the following example:
<hGrid ... >
<nodeStamp>
<column>
<columnHeader>
Color
</columnHeader>
</column>
</nodeStamp>
...
</hGrid>
The following example makes use of the
nodeStamp
to stamp out a custom element in the object hierarchy column:
<hGrid ... >
<nodeStamp>
<column>
...
<contents>
<flowLayout>
<contents>
<messagePrompt messageType="info"/>
<text data:text="text"/>
</contents>
</flowLayout>
</contents>
</column>
</nodeStamp>
...
</hgrid>
Until now, all the example HGrids have been static (non-interactive). In
order to make the HGrid interactive an
HGridDataProxy
must be used (the concept of a proxy was
introduced in a previous section describing the
TreeBean
). The proxy is responsible for handling the
expand, collapse, focus and breadcrumb links of the HGrid. In order to animate
the HGrid, the proxy needs to maintain the HGrid state across requests.
oracle.cabo.ui.data.tree.ClientStateHGridDataProxy
is an
instance of such a proxy that maintains the state on the client side by
encoding the URL. Here is an example of creating a
ClientStateHGridDataProxy
in a UIX Controller event
handler, and storing the proxy on the
EventResult
(event handlers are described in the UIX
Controller chapter):
package oracle.cabo.doc.demo;
public class HGridDemo
{
public static EventResult doHGridEvent(BajaContext bc, Page page,
PageEvent event)
{
HGridDataProxy hGridProxy = new ClientStateHGridDataProxy();
EventResult result = new EventResult(page);
result.setProperty("hGridProxy", hGridProxy);
return result;
}
}
In order for the above code to be called, the UIX file needs to register the above method as the default event handler, as in the following code snippet:
<handlers>
<event name="*">
<method class="oracle.cabo.doc.demo.HGridDemo"
method="doHGridEvent"/>
</event>
</handlers>
Finally, the proxy created in the event handler and stored on the
EventResult
must be data bound to the
proxy
attribute of the HGrid. The following is an
example of this process (note that the XML namespace prefix
data
is
bound to the UIX Components namespace
http://xmlns.oracle.com/uix/ui
,
and
ctrl
is bound to the UIX Controller namespace
http://xmlns.oracle.com/uix/controller
):
<hGrid id="hg1" data:treeData="nodes@treeData"
data:proxy="hGridProxy@ctrl:eventResult">
Now the expand/collapse/focus links on the HGrid come alive! However, there is
some more work that needs to be done before we can play with it. The
ClientStateHGridDataProxy
generates certain UIX
Controller events that must be handled on the server side. In addition, four
event parameters may be generated; the following table summarizes these event
parameters:
Event Parameter
|
Description | |
---|---|---|
uiXML | UIConstant | |
source
|
SOURCE_PARAM
|
The ID of the HGrid that generated this event |
state
|
STATE_PARAM
|
The current expand/collapse state of the HGrid |
root
|
ROOT_PARAM
|
Identifies the current focus root of the HGrid |
node
|
NODE_PARAM
|
Identifies a tree node that must be expanded/collapsed |
The following table describes the events triggered by the
ClientStateHGridDataProxy
, and lists the event
parameters accompanying each event:
Event
|
Event Parameters
|
Description | ||||
---|---|---|---|---|---|---|
uiXML | UIConstant | source | state | root | node | |
focus
|
FOCUS_EVENT
|
X | X | X | Change the focus of the HGrid | |
expand
|
EXPAND_EVENT
|
X | X | X | X | A tree node must be expanded/collapsed |
expandAll
|
EXPAND_ALL_EVENT
|
X | X | X | Recursively expand all the tree nodes beneath the current focus root | |
collapseAll
|
COLLAPSE_ALL_EVENT
|
X | X | X | Recursively collapse all the tree nodes beneath the current focus root |
Each of these four events needs to be handled on the server. A
ClientStateHGridDataProxy
needs to be created in each case; it is just a question of which constructor to call. The following code
is used to handle each of the HGrid events (for more information about the
constructors, see the
ClientStateHGridDataProxy
JavaDoc):
public static EventResult doHGridEvent(BajaContext bc, Page page,
PageEvent event)
{
HGridDataProxy hGridProxy;
if (event!=null)
{
String state = event.getParameter(UIConstants.STATE_PARAM);
String root = event.getParameter(UIConstants.ROOT_PARAM);
String node = event.getParameter(UIConstants.NODE_PARAM);
String eventName = event.getName();
if (eventName.equals(UIConstants.COLLAPSE_ALL_EVENT))
{
hGridProxy = new ClientStateHGridDataProxy(state, root, false);
}
else if (eventName.equals(UIConstants.EXPAND_ALL_EVENT))
{
hGridProxy = new ClientStateHGridDataProxy(state, root, true);
}
else if (eventName.equals(UIConstants.FOCUS_EVENT))
{
hGridProxy = new ClientStateHGridDataProxy(state, root);
}
else // eventName.equals(UIConstants.EXPAND_EVENT)
{
hGridProxy = new ClientStateHGridDataProxy(state, root, node);
}
}
else // there is no event. This is the initial state.
{
hGridProxy = new ClientStateHGridDataProxy();
}
// create an EventResult such that the current page is rendered again, in
// response to the event
EventResult result = new EventResult(page);
// set the HGrid proxy as a property on the EventResult
result.setProperty("hGridProxy", hGridProxy);
return result;
}
The HGrid has an expand/collapse-all feature that is useful most of the time;
however, in some cases the number of elements in a subtree can be huge -
performing an expand-all on such a subtree would lead to undesirable
results. Therefore, the
ClientStateHGridDataProxy
exports a way to disable expand/collapse-all for a given node. It recognizes
the key
ClientStateHGridDataProxy.EXPAND_ALL_KEY
(or
expandAll
in uiXML). If the value of this key
is
Boolean.FALSE
(or
false
in uiXML) then no expand/collapse-all
links are generated for that tree node.
<data name="treeData">
<inline>
<nodes text="All Colors" expandAll="false" expandable="collapsed">
... <!-- lets say there are tons and tons of subnodes -->
</nodes>
</inline>
</data>
It is possible to create an HGrid that is initially focused on some subnode
(including a leaf node), by using the
ClientStateHGridDataProxy(int[] focusPath)
constructor. The array of
int
s is a path to the
subnode that is the focus. Each element is a (zero based) child index of the
next node, on the path from root to focus node. The first element is a child
index of the root node; it produces the next node, A, on the path. The next
element is a child index of node A, and it produces node B, and so on. For
example, to focus in on the 3rd child of the 1st child of the root use:
// Starting from the root, follow the first child (ie: index zero)
// and then follow the 3rd child (ie: index 2)
int[] focusPath = {0, 2};
hGridProxy = new ClientStateHGridDataProxy(focusPath);
In some cases the tree displayed by the HGrid is small enough that the
focusing capability is unnecessary. The focus column (and the breadcrumbs) may
be turned off by calling
setBreadCrumbsEnabled(false)
on the
ClientStateHGridDataProxy
instance. Note that this can
only be done if the HGrid is focused on the root (the default focus). If the
HGrid is focused on some subnode, the breadcrumbs should not be disabled, as
the user would have no way of focusing out (in fact, an exception will be
thrown if the breadcrumbs are disabled in this case).
Please see the Table chapter for detailed descriptions on how to retrieve data from the HGrid, as the HGrid uses the same mechanisms. This includes using form submitted mode, and retrieving selection data on both the client and server sides.
This chapter discussed tree-structured data and looked at ways
of creating such trees in UIX. It also introduced the
TreeBean
,
BrowseMenuBean
and
HGridBean
and discussed how these are simply
alternate ways to view tree-structured data. Finally, it told how to use these beans and the UIX Controller to provide interactivity.