Version 5: Refining Hierarchy Selection UI and Query Axis Editing

Thursday, February 02, 2012

You can download this version of the sample, and an improved version of the component library from here.



Sample page including the <olap:queryAxisEditor> component
This version replaces the previous listbox and buttons used to add hierarchies to the query axes with a new composite component that:
  • Displays the current cube's hierarchies organized by the axis where they are used. Unused hierarchies are displayed in an "Unused"
    axis.
  • Allows for hierarchy sorting (within the ROWS and COLUMN axes).
  • Allows hierarchy removal from an axis.
  • Allows moving used hierarchies to other query axes.
  • Keeps a selected hierarchy (in the sample page this selected hierarchy is the one displayed by the query hierarchy editor)
  • Includes a button to swap rows and columns axes.
  • Allows selecting of the non-empty state for ROWS and COLUMNS axes.
All this functionality is added to the sample page with the following facelet snippet
<olap:queryAxisEditor
id="axisEditor"
query="#{queryBean}"
value="#{queryBean.selectedHierarchyName}">
<f:ajax
event="change"
execute=":form:axisEditor"
render=":form:axisEditor :form:hierarchyEditor"/>
<f:ajax
event="edit"
render="@all"/>
</olap:queryAxisEditor>

<olap:queryAxisEditor> Composite Component Internals

The queryAxisEditor composite component takes the following parameters:
  • query: AbstractQueryBean derived managed bean keeping the query state
  • value: backing bean property used to keep the current selected hierarchy name
This component can fire two client side events:
  • change: when the user changes the currently selected hierarchy
  • edit: when the user clicks on any action button (remove, add to rows, add to columns, add to filter, push up or push down) changing the current query.
This is the facelet used to implement this composite component
<composite:interface>
<composite:attribute
name="query"
/>
<composite:attribute
name="value"
/>
<composite:attribute
name="styleClass"
/>
<composite:attribute
name="headerClass"
/>
<composite:attribute
name="rowClasses"
/>
<composite:clientBehavior
name="change"
default="true"
event="click"
targets="columns rows filter unused"
/>
<composite:clientBehavior
name="edit"
event="edit"
targets="columns rows filter unused"
/>
<composite:clientBehavior
name="edit"
targets="swap"
/>
</composite:interface>
<composite:implementation>
<div
id="#{cc.attrs.clientId}">
<h:commandButton
id="swap"
styleClass="cgaofHierarchyListSwapBtn"
value="Swap Axes"
action="#{queryAxisEditorBean.swapAxes(cc.attrs.query)}"/>
<h:inputHidden
id="selected"
value="#{cc.attrs.value}"/>
<olap:queryAxisEditorBase
query="#{cc.attrs.query}"
value="#{cc.attrs.value}"
valueControl="#{cc.attrs.clientId}:selected"
axisName="COLUMNS"
id="columns"
styleClass="#{cc.attrs.styleClass}"
headerClass="#{cc.attrs.headerClass}"
rowClasses="#{cc.attrs.rowClasses}"/>
<olap:queryAxisEditorBase
query="#{cc.attrs.query}"
value="#{cc.attrs.value}"
valueControl="#{cc.attrs.clientId}:selected"
axisName="ROWS"
id="rows"
styleClass="#{cc.attrs.styleClass}"
headerClass="#{cc.attrs.headerClass}"
rowClasses="#{cc.attrs.rowClasses}"/>
<olap:queryAxisEditorBase
query="#{cc.attrs.query}"
value="#{cc.attrs.value}"
valueControl="#{cc.attrs.clientId}:selected"
axisName="FILTER"
id="filter"
rendered="#{cc.attrs.query.slicerHierarchies.size() > 0}"
styleClass="#{cc.attrs.styleClass}"
headerClass="#{cc.attrs.headerClass}"
rowClasses="#{cc.attrs.rowClasses}"/>
<olap:queryAxisEditorBase
query="#{cc.attrs.query}"
value="#{cc.attrs.value}"
valueControl="#{cc.attrs.clientId}:selected"
id="unused"
styleClass="#{cc.attrs.styleClass}"
headerClass="#{cc.attrs.headerClass}"
rowClasses="#{cc.attrs.rowClasses}"/>
</div>
</composite:implementation>

I'm using an inputHidden control to keep the selected hierarchy name: its value is changed by the inner queryAxisEditorBase controls, responsible for displaying the contents of a single axis. Those controls receive the query bean, the current selected hierarchy name, the client id of the hidden input field and the name of the axis they are displaying. Each axis is displayed using a table:
  • The axis name and the non-empty toggle button are displayed within the table's header.
  • Each hierarchy within the axis is displayed in a row with three columns:
    • A column for the remove button.
    • A column for the hierarchy's caption. The caption includes javascript to change the selected hierarchy on clicking.
    • A column for the other action buttons: rows, columns, filter, push up and push down.
Both composite components (queryAxisEditor and queryAxisEditorBase) relay on the QueryAxisEditorBean managed bean to execute actions and simplify facelets.

Required Query Package Improvements

The improvements implemented in the query package have been straightforward and all of them consisted in implementing methods already defined by the org.olap4j.query package:
  • Query.swapAxes to allow axes swapping
  • QueryHierarchy.isNonEmpty(), QueryHierarchy.setNonEmpty() to check/set the flag controlling the generation of NON EMPTY axis.
  • QueryHierarchy.pushUp(), QueryHierarchy.pushDown()to allow moving a hierarchy within an axis.