For some reason, Sencha decided to NOT make every single component in their architecture raise a click event. I don’t comprehend this, but I’m sure they had good reasons – possibly performance, possibly scalability.
Whatever the reason, if you want to make anything clickable, it’s very simple. Simply add the following listener.
id : 'myLabel',
xtype : 'label',
html : '<div class="myHelpLabel"></div>',
listeners : {
render : function(c) {
c.getEl().on('click', function(){ this.fireEvent('click'); }, c);
}
}
Poof. Now you can detect when that element is clicked in your controller (if you’re using the Sencha MVC architecture).
init: function(){
this.control({
'sampleView #myHelpLabel' : {
click : function(c) {
// Do something brilliant
}
},
...
More of a personal post for future reference, but in case anyone else stumbles across this, let me know if you have a better way of doing this.
I’m writing an application with ExtJS 4 and when the user clicks on the close button a Window header, I simply want to close it and reset the state on the parent window. However, when the user clicks on the Save button, I want to perform some other actions and then close the child window and the state of the parent window’s controls will be something else.
My problem is that in the MVC architecture defined in ExtJS 4, I only had the child window’s close event on which I could listen – that event is fired when the user clicks on the header icon, and when I execute window.close() in the save method’s logic.
Anyway, the only way I could capture when the user clicked on the header close icon was to override the Ext.panel.Panel method initTools(). I simply copied the entire method from ext-debug-all.js and changed which method handles the header button click gesture. I then added the headerClose() method which is basically the close() method but with an added event being fired – headerclose.
Ext.define('MyApplication.Child.Window' ,{
initTools : function() {
...
if (me.closable) {
me.addClsWithUI('closable');
me.addTool({
type: 'close',
handler: Ext.Function.bind(me.headerClose, this, [])
});
}
...
},
headerClose: function() {
if (this.fireEvent('beforeclose', this) !== false) {
this.fireEvent('headerclose', this);
this.doClose();
}
}
};
Now, in my controller I can handle that gesture specifically while handling the more general close event in a different way.
/*
* Action handler for when the user clicks on the window close button
* on the child window, while the general close event does nothing
*/
'childWindow': {
headerclose: function() {
this.resetParentState();
},
close : function() {
// Just a stub to show nothing is done on close()
}
},
'saveButton': {
click : function(button) {
doSomethingVeryCool();
button.up('window').close(); // This will not reset the state of parent window
}
}
I’ll be giving a hands-on demonstration and overview of the ExtJS 4.0 framework. We’ll start from the very basics, in code, to see all of the features on the new version, including:
The meeting is currently scheduled to be on July 6th, but I know that’s a big vacation week for people, so if the RSVP level is low, I’ll reschedule for later in the month.
Pittsburgh JavaScript Developers: Building Web Applications With ExtJS 4
Related Article(s): AjaxCFC ported to ExtJS
My integration of ExtJS, ajaxCFC and ColdFusion continues.
Note: This is for version 3.3.1. I’ve also been happily playing around with version 4, but until it’s officially released, I must continue building with 3.3.1.
Anyway, here’s what I was trying to solve. When I want to allow users to serach for items from a ComboBox, I configure a store, and inside the store, I configure an Ext.data.HttpProxy to hit a separate ColdFusion page that performs a query and returns the results.
var WidgetStore = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({
url: 'liveQueries/widgets.cfm'
}),
reader: new Ext.data.JsonReader({
root: 'data',
totalProperty:'totalRecords'
}, Widget)
});
<cfsetting enablecfoutputonly="true">
<cfparam name="query" default="">
<cfquery name="chargeCodes" datasource="#getDatasource().getName()#">
SELECT UNIQUE widget_seq_no, identifier, descr
FROM widgets
WHERE (REGEXP_LIKE(identifier,<cfqueryparam cfsqltype="cf_sql_varchar" value="^#query#">,'i')
OR REGEXP_LIKE(descr,<cfqueryparam cfsqltype="cf_sql_varchar" value="^#query#">,'i'))
AND widget_type_cd='SNAPPYDS'
ORDER BY identifier asc
</cfquery>
<cfscript>
jsonBean = createobject("component","ajaxCFC.JSON");
jsonEncodedCriteria = jsonBean.encode(data=chargeCodes, queryFormat="array");
writeOutput(jsonEncodedCriteria);
</coldfusion>
<cfsetting enablecfoutputonly="false">
I hate this, because I already am using my Ext.AjaxCFC class to query my ColdSpring beans asynchronously, so why do I have to set up these standalone ColdFusion pages to performs queries that should be executed in the component?
I decided to take matters into my own hands, and I have to give the ExtJS team props again, because it was easy to accomplish by extending the Ext.data.MemoryProxy class. As you can see, all I need to do is override the doRequest() method to execute Ext.AjaxCFC.request() with the data passed in during initialization.
Ext.ns('Ext.ux.data');
Ext.ux.data.AjaxCFCProxy = Ext.extend(Ext.data.MemoryProxy, {
constructor : function(data){
Ext.ux.data.AjaxCFCProxy.superclass.constructor.call(this);
this.data = data;
},
doRequest : function(action, rs, params, reader, callback, scope, arg){
this.data.data.query = params.query;
Ext.AjaxCFC.request({
bean : this.data.bean,
method : this.data.method,
data : this.data.data,
success: function(rs) {
var result = reader.readRecords(rs);
callback.call(scope, result, arg, true);
},
error: function(results) {
Ext.MessageBox.alert('Load Failed', 'Unable to load requested data');
}
});
}
});
WidgetSearch = new Ext.form.ComboBox({
minChars: 2,
loadingText: '',
itemSelector: 'div.search-item',
triggerClass: 'x-form-search-trigger',
emptyText: '',
width: 300,
listWidth: 0,
store: new Ext.data.Store({
// I've extended the base Ext.data.MemoryProxy()
// class to use the AjaxCFC object for searching
// instead of having to use a liveQuery. Much cleaner.
proxy: new Ext.ux.data.AjaxCFCProxy({
bean : 'Widget',
method : 'search',
data : { source_table : 'WIDGET' }
}),
Did you know that you can use the table layout in a ExtJS Toolbar? I didn’t until a few days ago. Not only that, but they provide the ability to have a large icon representing a button with a menu, kinda like how the new version of Microsoft Office programs do it.
Brain? Open the floodgates.
I’m still working on redesigning an application at work while the project has spun, yet again, back to the requirements stage. Frustrating? A little, but it does give me time to try some cool things with the user interface and clean up some code.
Anyway, here’s a nice, clean, but boring toolbar I have for this application. Not bad, but there’s just no pizzazz. It’s very corporate looking. I decided to try out the table format and use some icons from our stock library to add some of that pizzazz it’s missing.
Here’s how it looks with all the cool icons, and menu buttons. The only drawback is it takes up 60 more pixels of my vertical real estate in the application. Therefore, for the users who don’t like all the frilly colors, and fancy doodads, or simply dislike how much space it takes up, I provided a menu item in the Help menu to toggle between the two!
Some sanitized code to see how this is done. First, I added each icon that I wanted to use as a class in my application’s CSS library.
...
.icon-help {
background-image:url('../images/icon-help.png') !important;
}
.icon-dollar {
background-image:url('../images/icon-dollar.png') !important;
}
...
Then I set up an Ext.ButtonGroup with a table layout. In this sample code, I’m using three columns of icons. In the first column is a large icon button with a sub-menu. The second row contains the same format. The third row contains two smaller icons aligned vertically. This is accomplished by specifying rowspan : 2 on each of the larger icon button in the first two columns.
/*
* S A M P L E M E N U
*/
var SampleButtonMenu = new Ext.ButtonGroup({
title : 'Sample',
columns : 3,
layout : 'table',
height : 80,
items : [{
text : 'Hierarchy',
iconCls : 'icon-hierarchy',
rowspan : '2',
scale : 'medium',
arrowAlign : 'bottom',
iconAlign : 'top',
width : 50,
menu : HierarchyMenu
},{
text : 'Inclusion',
iconCls : 'icon-treatment',
rowspan : '2',
scale : 'medium',
arrowAlign : 'bottom',
iconAlign : 'top',
width : 40,
menu : [{
text: 'Sample Inclusion Button Menu',
handler: function(){ document.location.href = 'index.cfm?sampleInclusion'; }
}]
},{
iconCls : 'icon-route',
tooltip: 'Route',
handler: function(){ document.location.href = 'index.cfm?routeSetup'; }
},{
iconCls : 'icon-bandage',
tooltip: 'Access Mapping',
handler: function(){ document.location.href = 'index.cfm?accessMapping'; }
}]
});
Since this is a simple user preference and has no other impact on the application, I decided to store their choice in a cookie. Here’s the very simple code below. Now, the first thing you might notice is I’m refreshing the page when the user toggles between the two.
Unfortunately, I had no choice in this because I tried using Toolbar.removeAll(), then adding all the menu items, then Toolbar.render(‘element’), and then Toolbar.doLayout() and this worked fine if I toggled once. If I toggled more than once, ExtJS choked on properly rendering the sub-menus for the buttons or top-level menus. No idea why, so I’m stuck with reloading the page.
/*
* D E T E R M I N E M E N U S I Z E
*
* This is stored in a cookie. If the cookie doesn't exist, default to small menu.
*/
var MenuSize = Ext.util.Cookies.get('MenuSize');
if (MenuSize == null) {
Ext.util.Cookies.set('MenuSize', 'small');
MenuSize = 'small';
}
/*
* H E L P M E N U
*/
var AboutWindow;
var HelpButtonMenu = new Ext.ButtonGroup({
id: 'LargeHelpMenu',
title : 'Help',
layout : 'table',
height : 80,
items : [{
text : 'Charm Help',
iconCls : 'icon-help',
scale : 'medium',
arrowAlign : 'bottom',
iconAlign : 'top',
width : 50,
menu : [{
text : 'Minimize Toolbar',
handler: function(){
Ext.util.Cookies.set('MenuSize', 'small');
document.location.href = document.location.href;
}
},{
text: 'About',
handler: showAboutWindow
}]
}]
});
var HelpMenu = new Ext.menu.Menu({
id: 'SmallHelpMenu',
items: [{
text : 'Maximize Toolbar',
handler: function(){
Ext.util.Cookies.set('MenuSize', 'large');
document.location.href = document.location.href;
}
},{
text: 'About',
handler: showAboutWindow
}]
});