Thursday, December 25, 2008

Disabling buttons based on table selection

The easiest way to do this is to simply put an EL expression in the disabled attribute of the button that checks the size of the table's selectedRowKeys. You would also need to set a partialTrigger on the button with the ID of the table. The only problem with this is that it requires a trip to the server to evaluate the expression. Our application is going to be used by customers that are going to have low bandwidth and therefore need as few trips to the server as possible. So, we had to come up with a way to disable our toolbar buttons on the client (i.e. through javascript). We knew that a panelCollection did this very thing, so we just needed to tap into the panelCollection's functionality in order to duplicate this behavior.

We also had a need to arrange the buttons on the toolbar differently than what the panelCollection allows for. So, using a template (to replace all of our current implementations of panelCollections) we overhauled the panelCollection to suit our needs.

We tapped into the AdfDhtmlPanelCollectionPeer and found two important methods, updateStandardMenuToolbarItems and updateRowSelectionContext. The first is called when a column is selected, and the second is called when a row is selected.

Our template basically hid the original toolbar and added one on top that we could customize as much as we wanted, one that gets updated by chaining our own custom methods onto the two mentioned above. I am sure there is a proper design pattern named for what we're doing; it's something that is done with the onload function in many apps. Anyway, here is the code that we used to get our client code called every time a row or column is selected on a table that is within our template (which of course includes a panelCollection):

/* Allows the 'extra' toolbar to function as if it is part of the
* panelcollection. */
function initGridScript() {
if (typeof AdfDhtmlPanelCollectionPeer.prototype.updateStandardMenuToolbarItemsORIG != 'function') {
AdfDhtmlPanelCollectionPeer.prototype.updateStandardMenuToolbarItemsORIG = AdfDhtmlPanelCollectionPeer.prototype.updateStandardMenuToolbarItems;

AdfDhtmlPanelCollectionPeer.prototype.updateStandardMenuToolbarItemsCustom = updateMenuButtons;

//Update the 'extra' toolbar's buttons when the normal toolbar is updated
AdfDhtmlPanelCollectionPeer.prototype.updateStandardMenuToolbarItems = function () {
this.updateStandardMenuToolbarItemsORIG();

if(typeof this.getDomElement().className != 'undefined'){
if (this.getDomElement().className.search('CustomGridWithToolbar') > -1) {
this.updateStandardMenuToolbarItemsCustom();
}
}
}

AdfDhtmlPanelCollectionPeer.prototype.updateRowSelectionContextORIG = AdfDhtmlPanelCollectionPeer.prototype.updateRowSelectionContext;

AdfDhtmlPanelCollectionPeer.prototype.updateRowSelectionContextCustom = updateTableButtons;

AdfDhtmlPanelCollectionPeer.prototype.updateRowSelectionContext = function () {
this.updateRowSelectionContextORIG();

if(typeof this.getDomElement().className != 'undefined'){
if (this.getDomElement().className.search('CustomGridWithToolbar') > -1) {
this.updateRowSelectionContextCustom();
}
}
}
}
}


The AdfDhtmlPanelCollectionPeer.prototype.updateStandardMenuToolbarItemsORIG = AdfDhtmlPanelCollectionPeer.prototype.updateStandardMenuToolbarItems; saves off the original functionality so that we can invoke it later.

The typeof AdfDhtmlPanelCollectionPeer.prototype.updateStandardMenuToolbarItemsORIG != 'function' ensures that we are not appending our own functions more than once.

The this.updateStandardMenuToolbarItemsORIG(); ensures the original functionality is invoked.

The check for 'CustomGridWithToolbar' ensures that we are only calling our custom code when the panelCollection is the one from our template.

This function, initGridScript(), is called at the bottom of the javascript file on which it is declared, and the javascript file itself included as part of the template. So, it gets called whenever the template is written out to the response.

The updateStandardMenuToolbarItemsCustom method disables/enables menu items based column selection. In updateRowSelectionContextCustom, the code checks the RichTable client component's getSelectedRowKeys() to disable and enable the buttons in the toolbar accordingly.

Thursday, December 11, 2008

How do I tell if they clicked a row in the table versus its body?

In our app, we have a dblClick clientListener as a direct child of a table. If the user clicks a row, we want to show them the edit popup for that row, if they click in white space (i.e. the are beneath the rows), we want to create a new row and show the popup to edit that new row. The source for either click will be the table itself, however, the target will be what we are interested in. To ensure that our code remains cross-browser compatible, we'll use methods made available by client-side ADF.

From the ADF js file, I have plucked the following:

var agent = AdfAgent.AGENT, domEvent = evt.getNativeEvent();
var target = agent.getEventTarget(domEvent);
var tablePeer = table.getPeer();
var attrs = tablePeer.GetRowKeyAndRow(target,tablePeer.getDomElement());


If attrs is null, then the user clicked within the table body and not on a row. If attrs is not null then the user clicked on an actual row.

Monday, December 1, 2008

Where'd my frickin' log window go?

I lost my log window in JDev, and no matter how many times I hit 'View > Log', it never appears; found the answer to resurrecting it on Duncan Mills' site.

  1. Shutdown JDev

  2. Delete windowinglayout.xml. This file is in the o.ide folder under the jdeveloper system folder, system11.1.1.0.31.51.56.

  3. Restart JDev


This file will get rebuilt with the layout defaults, which includes the log window, or any other window you may have misplaced, when JDeveloper restarts.