JHeadstart 10.1.3 has several new features, a whole new architecture and support for ADF Faces. One of the most interesting new concepts is the notion of the Custom Templates, that allow us to extend and override the functionality of the Application Generator with our own generation capabilities. Almost any post generation change can now be included in the generator and thus become generatable. In our experience this has proven simple to accomplish and hugely beneficial to the productivity as well as the maintainbility of our project and application.
In this article, I will describe the implementation of a new table overflow style, and how it was incorporated into the JHeadstart Application Generator. The article provides the custom templates for download, so you can benefit from this functionality yourself.
JHeadstart 10.1.3 supports three table overflow styles: when a group has more items than comfortably fit into a table, you use an overflow for items that do not have to be visible as summary items directly in the table rows, but that ideally are close by. The inline overflow allows the user to disclose details – expand a select row to reveal the overflow items. The overflow styles right and below display overflow items for the currently selected record, either next to or below the table. One disadvantage of these last two overflow styles is that they always take up real estate on the screen, even if you have no particular interest in them. Especially for larger numbers of overflow items, this may not be ideal.
During Steven Davelaar’s presentation the other day – see AMIS Query – JHeadstart – Launch of 10.1.3 and 5th Anniversary – I had visions of a new table overflow style, dubbed Bubble, that would allow a user to bring up a popup – not a window but a floating div – with the overflow items, much like an overflow right on demand. It would look something like:
When the mouse is moved over one of the … (detail) icons, the overflow bubbles up:
Some inspiration for how to implement a popup element in a JSF page in the first page came from this article: Integrating ADF Faces and MyFaces Tomahawk – Creating a Popup with ADF Faces Shuttle Component . The first steps of generating a page with table layout using JHeadstart 10.1.3 are not discussed here. See the JHeadstart Developer’s Guide or many other articles out there. Instructions on setting up MyFaces Tomahawk with your project are described in the article referred to above, on creating a Popup for a Shuttle Component.
The steps required for implementing this functionality as a post generation modification are:
- generate the table layout like always using JHeadstart 10.1.3, using the table overflow right style
- prepare the project for use of MyFaces Tomahawk components (see the article above). This includes: download and install tomahawk.jar, add library and taglib to the project, install some Apache Jakarta Commons libraries and add the MyFaces Extension Filter to the web.xml file. Note: this may sound formidable, but will take less than 10 minutes.
- add the Tomahawk Taglib uri to the JSPX page
- create a small JavaScript library and reference it from the JSPX page
- wrap the overflow right elements in a Tomahawk Popup Element
- add a column to the table to contain the icon that will launch the popup
Once these steps are complete, we can run the application and use the popup overflows. However, if we were to generate the application afresh, we would lose our modifications. Using custom templates, we can embed these changes in the next round of generation or – even more bold – make this overfow style a generic feature available for all tables in our application. Let’s aim high and go for this second objective: we will extend the JHeadstart Application Generator so that we can toggle the bubble overflow on and off for every table, simply by setting a single (custom) property in the Application Definition.
Once the custom templates have been developed, all you need to do in order to generate the Bubble overflow (apart from step 2, preparing your project for Tomahawk) is the following:
1. Change the Template Properties file at the Application (Service) Level:
The Template Binding File – here set to AMISTemplateBindings.jtp – is a file that specifies the default templates to use when generating the application. We can override these settings at any level in the Application Definition file, but any generic changes in templates that apply to all our projects or at least the entire application ideally are configured in the Template Bindings file. I have created the AMISTemplateBindings.jtp file by copying the default file – defaultTemplateBindings.jtp in the templates/config directory under the ViewController project – and renaming it:
2. Specify for a Group with Table or Table-Form layout with overflow style right whether or not the Bubble Overflow style should be active:
It is easy to miss: what I have done here is set the value of the Custom Property 1 to bubble. The new templates will only be activated if the group layout is tale or table-form, the overflow style is right and the custom property 1 has the value bubble. Note that the name of Custom Property 1 is irrelevant to the Generator.
Extending the JHeadstart Application Generator
By just copying the defaultTemplateBindings.jtp to AMISTemplateBindings.jtp, I have not changed a single bit of functionality of course. It is time for that now. I have copied three different templates in order to start modifying them. I have AMISTemplateBindings.jtp refererring to those three:
- DATA_PAGE=default/page/AMISdataPage.vm – copy from
- TABLE_GROUP=default/pageComponent/AMIStableGroup.vm
- TABLE_DELETE_COLUMN=default/item/table/AMIStableDeleteColumn.vm
Modifying the dataPage template:
The AMISdataPage.vm template has two modifications: one to add the URI for Tomahawk to the page and the other one to include the JavaScript library that we need:
<?xml version='1.0' #ENCODING_PROP()?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="${JHS.service.jspVersion}"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:af="http://xmlns.oracle.com/adf/faces"
## AMIS: support for Bubble Overflow - add the Tomahawk Taglibrary URI to the page
#if (${JHS.current.group.property1}=='bubble')
xmlns:t="http://myfaces.apache.org/tomahawk"
#end
## AMIS: end support for Bubble Overflow
xmlns:afh="http://xmlns.orac le.com/adf/faces/html">
#if ($JHS.service.jspVersion=='2.0') <jsp:output omit-xml-declaration="true" doctype-root-element="HTML"
doctype-system="http://www.w3.org/TR/html4/loose.dtd"
doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"/> #else
<jsp:text>
<![CDATA[ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> ]]>
</jsp:text>
#end
<jsp:directive.page contentType="text/html;charset=${JHS.encoding}"/>
<f:view>
<afh:html id="html">
<afh:head title="#PAGE_TITLE()" id="head">
<meta http-equiv="Content-Type"
content="text/html; charset=${JHS.encoding}"/>
<link rel="stylesheet" href="${pageContext.request.contextPath}/jheadstart/css/jhsStyles.css" type="text/css" id="jhsStyles"/>
## AMIS: support for Bubble Overflow - include the JavaScript library
#if (${JHS.current.group.property1}=='bubble')
<afh:script source="/jheadstart/BubbleUtils.js"/>
#end
## AMIS: end support for Bubble Overflow
</afh:head>
<afh:body id="body">
...
Modifying the tableGroup template:
The tableGroup template is changed in order to wrap the table overflow right element when the bubble value is set in the custom property:
</af:table>
#if($pageComponent.hasOverflowRight)
</afh:cellFormat>
<afh:cellFormat valign="top" id="${group.shortName}OverflowRightCell_2">
<af:objectSpacer width="10"/>
</afh:cellFormat>
<afh:cellFormat valign="top" id="${group.shortName}OverflowRightCell_3">
## AMIS: support for overflow bubble
#if (${JHS.current.group.property1}=='bubble')
<t:popup styleClass="popup" closePopupOnExitingElement="true"
closePopupOnExitingPopup="true" displayAtDistanceX="40"
displayAtDistanceY="-20">
<h:outputText value="" title="bubble"/>
<f:facet name="popup">
<af:panelBox>
#end
## AMIS: end support for overflow bubble
#JHS_PARSE("TABLE_OVERFLOW_AREA" ${JHS.current.model})
## AMIS: support for overflow bubble
#if (${JHS.current.group.property1}=='bubble')
</af:panelBox>
</f:facet>
</t:popup>
#end
## AMIS end support for overflow bubble
</afh:cellFormat>
</afh:rowLayout>
The modification is minimal: if custom property 1 is set to bubble (and the code is only ever reached when overflow style right is processed) then we create a (Tomahawk) popup element that wraps the overflow right elements inside a PanelBox inside its popup Facet. That is all!
Modifying the tableDeleteColumn template:
Here I have been a little lazy: since the column that will contain the icon the user can hover over or click on in order to open the bubble overflow is positioned to the right of the column with Delete? checkboxes – or as the right-most column at any rate – it was an easy approach to add the creation of this column to this template:
#if ($JHS.current.group.tableDeleteAllowed)
<af:column id="${JHS.current.group.shortName}DeleteColumn" headerText="#DELETE_COLUMN_HEADER()" formatType="icon">
<af:selectBooleanCheckbox binding="#{#COLLECTION_MODEL_BEAN().deleteCheckbox}"/>
</af:column>
#end
#if (${JHS.current.group.property1}=='bubble')
<af:column id="TableOverflowPopup" headerText="... "
formatType="icon">
<af:objectImage source="/jheadstart/images/detailsicon_enabled.gif" id="showBubble" onclick="bubbleUp(arguments[0]);" />
</af:column>
#end
In the template we test the value of the custom property 1. If it has the value bubble (and we do not explicitly test for layoutstyle table or table-form and overflow is right which we probably should be doing here), then we add an extra column. It has headerText … (can be changed to anything you like better), displays an icon already shipped with JHeadstart (again can be changed to any text or image you like better) and reacts to a click (again, can be changed to onmouseover to respond to hovering instead of clicking). Note that the JavaScript function bubbleUp is provided in the JavaScript library BubbleUtils.js added in the AMISdataPage.vm.
Note that the ‘bubble’ column can easily be created in the tableGroup template as well.
JavaScript library BubbleUtils
A crucial element in the bubble overflow is the BubbleUtils JavaScript library. It performs the following role: the Popup component from the Tomahawk library is combination of a single visible element that can be clicked on or hovered over in order to display the popup. If we want every row in the table to contain an icon to bring up the popup – while not actually having the popup duplicated in every row – , we have to use a little trick: there is only a single popup element with a single item to trigger its popup. The popup icons in the rows trigger the appropriate mouse event on the single popup item.
You can see this from the column definition above: the objectImage with id equals showBubble has an onClick (could also be an onMouseOver) that calls the bubbleUp function. The Tomahawk popup element was setup with an outputText with title equals bubble and no value: that means it is invisble, does exist and can be activated. The bubbleUp function will locate this element using the value of its title attribute. When found, the mouseover event is iniated on this invisible element. That in turn will bring up the popup! This trick is an application of the steps described in the article Programmatic Client Side Activation of JSF Components.
That is not yet enough. We need to do a second thing. You probably know that the normal Table Overflow Right (or Below for that matter) synchronizes with currently selected record. If we click on the radio button at the beginning of the table row, we select the record in that row and implicitly refresh (ADF Faces partial page refresh or AJAX in the way of ADF Faces) the overflow items.
function bubbleUp(e) {
... determine xcoord and ycoord with support for many browsers (see resource BubbleUtils.js below)
// take the currentTarget.id for example: TableWithBubbleOverflowTable:7:TableWithBubbleOverflowshowBubble
// replace the showBubble piece with TableWithBubbleOverflowSelectOne ( the id of the selection element)
// we need to trigger a click event on TableWithBubbleOverflowTable:7:TableWithBubbleOverflowSelectOne
doClick(document.getElementById(e.currentTarget.id.replace("showBubble","SelectOne")));
// now locate the span element with title=bubble as that is the element that can bring up the popup
links = document.getElementsByTagName('span');
for (var i=0; i<links.length; i++) {
if (links[i].title.indexOf('bubble') != -1) {
doEventDispatch(links[i], xcoord, ycoord);
}//if
}//for
}
function doEventDispatch(elm, xcoord, ycoord) {
fireMouseEvent(elm, 'mouseover', xcoord, ycoord);
}
function doClick(elm) {
fireMouseEvent(elm, 'click', 0,0);
}
... //the function fireMouseEvent can be found in the resource listed below.
Resources
Download the zip-file with three modified templates and the AMIS jtp file: AMIStemplatesTableOverflowBubble.zip
Article on programmatically manipulating JavaScript and DOM events: http://www.howtocreate.co.uk/tutorials/javascript/domevents
Download the BubbleUtils.js javascript library with client side activation of elements.
Of course you are right that using the MouseOver had some hazards: it may fire far more frequently than is the intention. However, I do not have the situation with the single button to show additional details: the icon in the last column for each record will trigger the popup. The same risk exists, that if you move the mouse over all these icons, the mouse over event fires even if you do not feel like it. And if your mouse continues to hover over an element, it may easily refire the event. So yes, caution is advised, and to be honest: I have used the onclick after the onmouseover gave me some headaches myself.
regards,
Lucas
Great post again. However, I have one question… I’m always troubled by the "mouseover" event because it can have some unpleasant effects. For instance imagine this situation: "your page is exactly the same as described in this post, meaning that the mouseover will set the currentrecord. Besides this you have ONE button to display additional information about the current record. This button is located above the table." In order to click the button you have to move your mouse to this button. If you go straight up to the button, a mouseover event will be triggered on every row that you pass during the way up…… meaning that you will get the additional information of the FIRST record. As far as i know, the only way to circomvent this behaviour is to navigate to the button using keys or to move your mouse outside the table and go up to the button. I prefer to use the onclick event which will set the current record. The user is in full control and not bothered by the unpleasant effects of the mouseover. Please correct me if I’m wrong, and please suggest any solutions… Thank for the post, which is (again) very usable. Luc Bors