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 can’t believe it took this long for this to click in my head. I’m sure I had seen the bubbleEvents() config option on ExtJS component documentation before, but my eyes just swept over it like when you’re looking for your kid in a crowd. the unimportant information immediately gets dismissed by your brain.
Well, today, I noticed an old bit of code in a Ext.Window object. Here’s an example:
MappingDialog = function(){
var dialog;
return {
init : function(){
dialog = new Ext.Window({
width: 200,
height: 150,
modal: true,
closable: false,
buttons: [
{ text:'Save', handler:function(){ dialog.fireEvent('saveMapping'); } },
{ text:'Cancel', handler:function(){ dialog.hide() } }
]
});
dialog.addListener('saveMapping', this.saveData, this);
},
saveData : function(){
... Save the data ...
},
showWindow : function(){ dialog.show(); },
hideWindow : function(){ dialog.hide(); }
}
};
Notice how I’m referencing the dialog object inside the Save and Cancel button handlers. That’s ugly, but at the time I wrote it I didn’t know how to do it differently. Today, however, I focused in on that dialog.fireEvent() statement and thought to myself, “Self, there has to be a way to bubble the event of a button up to its parent window, so I can avoid having direct references to the Window inside the Button – which is inside the Window.”
Off to the ExtJS API Docs and I immediately notice the bubbleEvents config option which, like I said, I’d glazed over hundreds of times in the past. remember? Well, using that, I now bubble any event up to its parent container which can handle it appropriately. Much more efficient code from a memory perspective and from a maintenance perspective as well, as renaming the Window won’t require a global search/replace.
MappingDialog = function(){
return {
init : function(){
dialog = new Ext.Window({
width: 200,
height: 150,
modal: true,
closable: false,
<strong>bubbleEvents: ['saveMapping'],</strong>
buttons: [{
text:'Save',
<strong>bubbleEvents: ['saveMapping'],</strong>
handler:function(){ this.fireEvent('saveMapping'); }
},{
text:'Cancel',
<strong>bubbleEvents: ['closeWindow'],</strong>
handler:function(){ this.fireEvent('closeWindow'); }
],
listeners: {
saveMapping : function() { this.fireEvent('saveMapping'); },
closeWindow : function() { this.close(); }
}
});
this.addListener('saveMapping', this.saveData, this);
},
saveData : function(){
... Save the data ...
},
showWindow : function(){ dialog.show(); },
hideWindow : function(){ dialog.hide(); }
}
};
I was browsing around the Sencha site this morning and saw that they announced the beta release of 3.3 which includes two major new components:
While those will garner much attention, the ugly stepchild mentioned at the bottom of the press release is also very cool. The ActionColumn allows you to put icons inside a grid with an accompanying event handler.
This is something we use quite a bit in Flex, and its addition to ExtJS is exciting.