The more we get our hands dirty with ExtJS the more we love how well it’s designed. It’s got a fantastic API and has been built for reusability, the speed at which we can deliver functionality is frightening. All it was missing was a good server side rendering engine and data store and you can’t get one better than the Oracle database and here’s another example of highlighting the beauty of APEX.
As stated previously we’re going to add a customizable option/button to our grid so we can change the settings in development mode at the individual grid region level. These will then update our page level JSON object which holds the grid meta data which is then fed to our grid widget when rendering. How we’ve done it is by simply merging two Ext examples together Hello World and Status Bar Advanced. We get both a nice modal popup window and dynamic form validation from the Ext code with no APEX page requirement and thus minimal integration and coding requirements (this means $$$$ savings).
The Ext form itself will have 15 parameters, using the APEX AJAX “On Demand Process” global variables found in wwv_flow as the input “name” which we will post to wwv_flow.show as documented by Mark Lancaster.
--------------------------------------------
-- Global input variables for AJAX utilities
--
g_widget_name varchar2(255);
g_widget_mod varchar2(255);
g_widget_action varchar2(255);
g_widget_action_mod varchar2(255);
g_widget_num_return varchar2(255);
g_x01 varchar2(32767);
g_x02 varchar2(32767);
g_x03 varchar2(32767);
g_x04 varchar2(32767);
g_x05 varchar2(32767);
g_x06 varchar2(32767);
g_x07 varchar2(32767);
g_x08 varchar2(32767);
g_x09 varchar2(32767);
g_x10 varchar2(32767);
We’ve simply built the entire form in Javascript and statically specified the available options that we will allow for customization on our grid. Simply because they won’t be changing anytime soon.
/*
* Ext JS Library 2.2.1
* Copyright(c) 2006-2009, Ext JS, LLC.
* licensing@extjs.com
*
* http://extjs.com/license
*
* APEX & Ext JS Integration Kit - 2.2.1
* Copyright(c) 2009-2010, e-DBA Ltd, LLC.
* apex-sig@e-dba.com
*
* http://www.e-dba.com/uk/f?p=111:APEXTJS_LICENSE
*/
var gridOptionsWindow;
Ext.QuickTips.init();
function gridOptions(pRegionID) {
if(!gridOptionsWindow){
// define widget URL
var u = (window.location.href.indexOf("?") > 0) ? window.location.href.substring(0,window.location.href.indexOf("?")) : window.location.href;
var baseURL = u.substring(0,u.lastIndexOf("/"));
var widgetURL = baseURL + '/wwv_flow.show?p_flow_id=' + Ext.getDom('pFlowId').value + '&p_flow_step_id='+$v('pFlowStepId')+'&p_instance=' + Ext.getDom('pInstance').value + '&p_request=APPLICATION_PROCESS=Ext.widget';
var gridOptionsForm = new Ext.FormPanel({
id: 'apextjs-grid-options-form',
renderTo: Ext.getBody(),
labelWidth: 80,
width: 300,
buttonAlign: 'right',
border: false,
bodyStyle: 'padding:10px 10px 0px 10px;',
defaults: {
anchor: '95%',
allowBlank: false,
selectOnFocus: true,
msgTarget: 'side'
},
items:[{
xtype:'hidden',
name: 'p_widget_name',
value: 'Ext.grid.options'
},{
xtype:'hidden',
name: 'p_widget_mod',
value: pRegionID
},{
xtype:'combo',
store:['true','false'],
name: 'x01',
fieldLabel: 'autoHeight',
blankText: 'autoHeight is required'
},{
xtype:'combo',
store:['true','false'],
name: 'x02',
fieldLabel: 'collapsible',
blankText: 'collapsible is required'
},{
xtype:'combo',
store:['true','false'],
name: 'x03',
fieldLabel: 'draggable',
blankText: 'draggable is required'
},{
xtype:'combo',
store:['true','false'],
name: 'x04',
fieldLabel: 'enableColLock',
blankText: 'enableColLock is required'
},{
xtype:'combo',
store:['true','false'],
name: 'x05',
fieldLabel: 'filter',
blankText: 'filter is required'
},{
xtype:'combo',
store:['true','false'],
name: 'x06',
fieldLabel: 'forceFit',
blankText: 'forceFit is required'
},{
xtype:'combo',
store:['true','false'],
name: 'x07',
fieldLabel: 'frame',
blankText: 'frame is required'
},{
xtype:'combo',
store:['true','false'],
name: 'x08',
fieldLabel: 'grouping',
blankText: 'grouping is required'
},{
xtype:'textfield',
name: 'x09',
fieldLabel: 'iconCls',
blankText: 'iconCls is required'
},{
xtype:'combo',
store:['true','false'],
name: 'x10',
fieldLabel: 'loadMask',
blankText: 'loadMask is required'
},{
xtype:'combo',
store:['true','false'],
name: 'p_widget_action',
fieldLabel: 'stripeRows',
blankText: 'stripeRows is required'
},{
xtype: 'numberfield',
name: 'p_widget_action_mod',
fieldLabel: 'width',
blankText: 'width is required'
}],
buttons: [{
text: 'Save',
handler: function(){
if(gridOptionsForm.getForm().isValid()){
var sb = Ext.getCmp('apextjs-grid-options-form-statusbar');
sb.showBusy('Saving form...');
gridOptionsForm.getEl().mask();
gridOptionsForm.getForm().submit({
url: widgetURL,
success: function(){
sb.setStatus({
text:'Form saved!',
iconCls:'',
clear: true
});
gridOptionsForm.getEl().unmask();
// Reload the page to see the changes
window.location.reload();
}
});
}
}
}]
});
var gridOptionsStatusBar = new Ext.Panel({
renderTo: Ext.getBody(),
title: 'Customize Grid Options',
width: 300,
autoHeight: true,
layout: 'fit',
items: gridOptionsForm,
bbar: new Ext.StatusBar({
id: 'apextjs-grid-options-form-statusbar',
defaultText: 'Ready',
plugins: new Ext.ux.ValidationStatus({form:'apextjs-grid-options-form'})
})
});
gridOptionsWindow = new Ext.Window({
renderTo: Ext.getBody(),
modal : true,
layout : 'fit',
width : 300,
autoHeight : true,
closeAction : 'close',
plain : true,
items : [gridOptionsStatusBar]
});
}
gridOptionsWindow.show();
}
We post the form to our Application Process called “Ext.widget” which simply reads the widget name and decides which handler PLSQL package.procedure to call. In this case our handler package will save the data into our application meta data table for individual grids allowing us to have custom options for each grid we create without changing any code. Once a success message is returned we simply reload the page to show the new grid config changes. So essentially APEX is just being used to handle the retrieving/saving of data, all securely of course!
Here’s an example of the form in action:

