org.araneaframework.uilib.tree.TreeWidget
allows representation of hierarchical data in a manner that has become
traditional in GUIs, as an expandable/collapsable tree. TreeWidget
represents trees' root node, which is special
in that it is not usually really rendered on-screen but serves as point where child nodes are attached.
Child nodes of TreeWidget
are TreeNodeWidget
s acquired from associated TreeDataProvider
or could be attached by the developer.
The TreeWidget
supports expanding and collapsing of all those nodes.
TreeDataProvider
is a simple interface with ability to return data belonging to any given node of the tree.
public interface TreeDataProvider extends Serializable {
/**
* Returns a list of child nodes for specified parent node.
*/
List<TreeNodeWidget> getChildren(TreeNodeContext parent);
/**
* Returns whether the specified tree node has any children.
*/
boolean hasChildren(TreeNodeContext parent);
}
As is apparent from the definition of TreeDataProvider
, descendants of the TreeWidget
that are to be presented in a tree, must be of type TreeNodeWidget
. TreeNodeWidget
is the superclass of TreeWidget
that also has child nodes and will be rendered too. Node rendering is done with
display widget that is passed to TreeNodeWidget
in its constructor.
/** Childless collapsed node, rendered by display widget. */
public TreeNodeWidget(Widget display);
/** Node with children. Expanded by default. */
public TreeNodeWidget(Widget display, List nodes);
/** Node with children, expand/collapse state can be set with corresponding flag. */
public TreeNodeWidget(Widget display, List nodes, boolean collapsed);
Display widget can be any widget that can render itself, it is rendered in the place of tree node instead of
TreeNodeWidget
, which is just a data holder. Very often, display widget is BaseUIWidget
which renders itself according to some JSP template.
TreeNodeWidget
does not accept independent TreeDataProvider
, its children are acquired
from TreeWidget
's TreeDataProvider
.
TreeWidget
enriches the Environment
with TreeContext
.
TreeNodeWidget
enriches the Environment
of its display widget
with TreeNodeContext
. Through these contexts display widgets have access to
owning tree node and root of the tree.
TabContainerWidget
manages widgets that are to be displayed and manipulated in separate tabs.
It provides basic operations like adding, removing, disabling, enabling and switching between tabs.
Its main operation mode is stateful, where switching between tabs preserves state in inactive tabs. It can be
made to operate statelessly (or with custom state management) by constructing new tab with WidgetFactory
instead of Widget
.
Following methods are available for adding tabs:
void addTab(String id, String labelId, Widget contentWidget);
void addTab(String id, Widget labelWidget, Widget contentWidget);
void addTab(String id, String labelId, WidgetFactory contentWidgetFactory);
void addTab(String id, Widget labelWidget, WidgetFactory contentWidgetFactory);
And for common tab operations:
boolean removeTab(String id);
boolean disableTab(String id);
boolean enableTab(String id);
boolean selectTab(String id);
For its children, TabContainerWidget is accessible from Environment
as TabContainerContext
.
Opens the tab container context and renders the labels for all tabs inside this container.
Renders the body of currently active (selected) tab. Must be used inside tab container context.
Renders specified tab container fully—writes out tab labels and active tab's content.
Widget that represents context menu content is called ContextMenuWidget
. By convention, it is
usually added to component hierarchy as a child of the widget for which it provides context menu.
widgetWithContextMenu.addWidget("contextmenu", new ContextMenuWidget(...));
ContextMenuWidget
sole constructor has a single ContextMenuItem
parameter.
ContextMenuItem
is a hierarchical container for menu items, consisting of menu entries and entry
labels. There are two types of menu entries: ContextMenuEventEntry
and ContextMenuActionEntry
—which respectively produce events (see Section 2.7.2, “Event Listeners”) or actions (see Section 2.7.3, “Action Listeners”)
upon selection of context menu item. Except for produced event type, these entries are constructed identically.
Creating context menu entry which tries to invoke widget event listener of someWidget
without supplying
any event parameters is done as follows:
ContextMenuEntry entry = new ContextMenuEventEntry("someEvent", someWidget);
When menu entry produced event requires some parameters,
javascript function must be defined that returns desired parameters. When left undefined,
function() { return null; }
is used. Sample javascript function which always
returns value of some fixed DOM element as event parameter looks like this:
var contextMenuEventParameterSupplier = function() {
// make sure that function call was really triggered by menu selection
if (araneaContextMenu.getTriggeringElement()) {
// supply value of DOM element 'someElement' as event parameter
return $('someElement').value;
}
return null;
};
Corresponding menu entry which detects and submits event parameters is created similarly to previous:
ContextMenuEntry entry = new ContextMenuEventEntry("someEvent", someWidget, "contextMenuEventParameterSupplier");
Whole construction of single multi-element and multi-level ContextMenuWidget
will look similar to this:
ContextMenuItem root = new ContextMenuItem();
// entry that produces event when clicked on
ContextMenuItem firstEntry =
new ContextMenuItem(
getL10nCtx().localize("someLabel"), // label
new ContextMenuEventEntry("someEvent", this));
// entry that just functions as submenu
ContextMenuItem secondEntry = new ContextMenuItem(getL10nCtx().localize("submenu"));
// action producing entry in a submenu
ContextMenuItem thirdEntry =
new ContextMenuItem(
getL10nCtx().localize("someOtherLabel"),
new ContextMenuActionEntry("someAction", this, "contextMenuActionParameterSupplier"));
secondEntry.addMenuItem(thirdEntry);
root.addMenuItem(firstEntry);
root.addMenuItem(secondEntry);
Registers the context menu in current template for widget with id
.
As one widget might be rendered in separate sections in a template, all these sections need to be identified so that
correct context menu can be detected at all times. This is done with <ui:widgetMarker>
tag
surrounding the widget sections.
Defines the surrounded section as belonging to a widget with id
. It writes out some
HTML tag with class
attribute value set to widgetMarker
.
<!-- Defines context menu for a ListWidget "list" -->
<ui:list id="list">
<ui:listFilter> ... </ui:listFilter>
<ui:listRows>
<!-- marker surrounding widget with identifier "list" -->
<ui:widgetMarker id="list" tag="tbody">
<ui:row id="${listFullId}.row${rowRequestId}">
<!-- cells -->
</ui:row>
</ui:widgetMarker>
</ui:listRows>
<!-- Context menu widget with conventional id -->
<ui:contextMenu id="list.contextmenu"/>
</ui:list>
Imagine that you have a big web page with input form, and you want certain input controls
to update something on that page as user changes the value of the control. Now, would it be
efficient if the value changes, its onchange
event submits the data so that an
OnChangeEventListener
could read it and return the same page with slight changes?
The main problem here is that a small change should not force the user wait until the page
reloads. Here is the part where partial rendering comes in.
Partial rendering is more thoroughly described by Alar Kvell's bachelor thesis Aranea Ajax. This section concentrates mostly on how a programmer can make partial rendering work.
First of all, a page must have a part (parts) that needs to be updated when an event occurs.
These regions are marked with the <ui:updateRegion>
tag by also indicating
its ID to reference it later.
It is not possible to update an HTML table cell. One needs to update the
entire row by using the <ui:updateRegionRows>
tag.
Next, one needs to specify the updateRegions
attribute of the input control
that has an event registered. The attribute value should contain a comma-separated list of
update region IDs that need to be update due to the event. It is important for this value to
be specified, because otherwise the entire page would be posted to the server.
When the input control has the updateRegions
attribute defined, Aranea will use
Ajax to send the form data to the server, invoke the OnEventListener
associated
with the event, and return the parts of the pages defined as update regions. Finally,
the script on the client side will replace the update regions on the page with the received
ones. For everything else on the same page, it will remain the same.
Note that these two steps described above are all that need to be taken to make partial rendering possible with Aranea.
Now let's take a look at a short example where partial rendering is used. The following is the code snippet from Aranea demo application component Easy AJAX w/ 'update regions'.
<ui:row>
<ui:formElement id="beastSelection">
<ui:cell styleClass="name">
<ui:label />
</ui:cell>
<ui:cell>
<ui:select updateRegions="ajaxBeasts"/>
</ui:cell>
</ui:formElement>
</ui:row>
<ui:updateRegionRows id="ajaxBeasts">
<c:if test="${not empty form.elements['concreteBeastControl']}">
<ui:row>
<ui:formElement id="concreteBeastControl">
<ui:cell styleClass="centered-name">
<ui:label />
</ui:cell>
<ui:cell>
<ui:checkboxMultiSelect type="vertical" />
</ui:cell>
</ui:formElement>
</ui:row>
</c:if>
</ui:updateRegionRows>
In the example, you can see that it does not matter in which order the update region
is declared and referenced. Also, because data (form elements) is displayed using table rows,
we must use <ui:updateRegionRows>
tag here to make it work. However,
the most important part of this example is that the <ui:select>
control defines the update region it wishes to update.
Currently file upload inputs don't work with update regions because the JavaScript cannot
read the unsubmitted file and serialize it to send it to the server. Therefore, if you
provide the updateRegions
attribute for a file upload input, the file won't
reach the server. We hope to find a solution to this limitation in near future.