YetAnotherFrank
25 May 2010, 12:04 AM
Ext version tested:
Ext 3.2.1
Adapter used:
ext
css used:
only default ext-all.css
Browser versions tested against:
FF3.6.3 (firebug 1.5.4 installed)
Operating System:
Windows Vista
Description:
An Ext.Action can be added to a toolbar or a menu instead of a button or menu item.
This leads to a button / menu item being created using the given Action.
However, the 'baseAction' property of that button / menu item does not reference the original Action, but a copy of that Action! This is quite annoying when you use a custom Action subclass calling its handler in its own scope, since the 'this' reference changes unexpectedly.
Test Case:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title id='title'>Title</title>
<!-- ** CSS ** -->
<!-- base library -->
<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
<!-- overrides to base library -->
<!-- ** Javascript ** -->
<!-- base library -->
<script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../../ext-all-debug.js"></script>
<!-- overrides to base library -->
<!-- extensions -->
<!-- page specific -->
<script type="text/javascript">
Ext.BLANK_IMAGE_URL = '../../resources/images/default/s.gif';
Ext.onReady(function(){
// Custom action class:
var MyAction = Ext.extend(Ext.Action, {
constructor: function(context) {
this.context = context;
MyAction.superclass.constructor.call(this, {
text: 'MyAction',
handler: this.doIt,
scope: this,
iconCls: 'blist'
});
},
doIt: function(){
Ext.MessageBox.alert('Click',"You clicked on 'Action 1', context: " + this.context);
},
setContext: function(context) {
this.context = context;
}
});
var action = new MyAction('initial data');
var tb = new Ext.Toolbar({
items: [
action // <-- Add the action as a button
],
renderTo: Ext.getBody()
});
var button = tb.items.get(0);
action.setContext("context injected through 'action'");
button.baseAction.setContext("context injected through 'baseAction'");
alert("Is it the same action object? " + (button.baseAction === action ? 'yes' : 'no'));
}); //end onReady
</script>
</head>
<body>
</body>
</html>
Steps to reproduce the problem:
Run sample code.
The result that was expected:
Alert: "Is it the same action object? yes"
When clicking 'MyAction' button: Alert: "You clicked on 'Action 1', context: context injected through 'baseAction'"
The result that occurs instead:
Alert: "Is it the same action object? no"
When clicking 'MyAction' button: Alert: "You clicked on 'Action 1', context: context injected through 'action'"
Debugging already done:
Yes
Possible fix:
Change Ext.Container#createComponent() to not apply config over a new object (the one containing the ownerCt reference). If the intention of your code was to not modify the config object, you could handle Ext.Action config objects ('isAction' property!) as a special case. The following code always modifies the config object and afterwards restores the original state by deleting 'ownerCt'.
Ext.Container.prototype.createComponent = function(config, defaultType){
if (config.render) {
return config;
}
// add in ownerCt at creation time but then immediately
// remove so that onBeforeAdd can handle it
config.ownerCt = this;
var c = Ext.create(config, defaultType || this.defaultType);
delete config.ownerCt;
delete c.initialConfig.ownerCt;
delete c.ownerCt;
return c;
};
Ext 3.2.1
Adapter used:
ext
css used:
only default ext-all.css
Browser versions tested against:
FF3.6.3 (firebug 1.5.4 installed)
Operating System:
Windows Vista
Description:
An Ext.Action can be added to a toolbar or a menu instead of a button or menu item.
This leads to a button / menu item being created using the given Action.
However, the 'baseAction' property of that button / menu item does not reference the original Action, but a copy of that Action! This is quite annoying when you use a custom Action subclass calling its handler in its own scope, since the 'this' reference changes unexpectedly.
Test Case:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title id='title'>Title</title>
<!-- ** CSS ** -->
<!-- base library -->
<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
<!-- overrides to base library -->
<!-- ** Javascript ** -->
<!-- base library -->
<script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../../ext-all-debug.js"></script>
<!-- overrides to base library -->
<!-- extensions -->
<!-- page specific -->
<script type="text/javascript">
Ext.BLANK_IMAGE_URL = '../../resources/images/default/s.gif';
Ext.onReady(function(){
// Custom action class:
var MyAction = Ext.extend(Ext.Action, {
constructor: function(context) {
this.context = context;
MyAction.superclass.constructor.call(this, {
text: 'MyAction',
handler: this.doIt,
scope: this,
iconCls: 'blist'
});
},
doIt: function(){
Ext.MessageBox.alert('Click',"You clicked on 'Action 1', context: " + this.context);
},
setContext: function(context) {
this.context = context;
}
});
var action = new MyAction('initial data');
var tb = new Ext.Toolbar({
items: [
action // <-- Add the action as a button
],
renderTo: Ext.getBody()
});
var button = tb.items.get(0);
action.setContext("context injected through 'action'");
button.baseAction.setContext("context injected through 'baseAction'");
alert("Is it the same action object? " + (button.baseAction === action ? 'yes' : 'no'));
}); //end onReady
</script>
</head>
<body>
</body>
</html>
Steps to reproduce the problem:
Run sample code.
The result that was expected:
Alert: "Is it the same action object? yes"
When clicking 'MyAction' button: Alert: "You clicked on 'Action 1', context: context injected through 'baseAction'"
The result that occurs instead:
Alert: "Is it the same action object? no"
When clicking 'MyAction' button: Alert: "You clicked on 'Action 1', context: context injected through 'action'"
Debugging already done:
Yes
Possible fix:
Change Ext.Container#createComponent() to not apply config over a new object (the one containing the ownerCt reference). If the intention of your code was to not modify the config object, you could handle Ext.Action config objects ('isAction' property!) as a special case. The following code always modifies the config object and afterwards restores the original state by deleting 'ownerCt'.
Ext.Container.prototype.createComponent = function(config, defaultType){
if (config.render) {
return config;
}
// add in ownerCt at creation time but then immediately
// remove so that onBeforeAdd can handle it
config.ownerCt = this;
var c = Ext.create(config, defaultType || this.defaultType);
delete config.ownerCt;
delete c.initialConfig.ownerCt;
delete c.ownerCt;
return c;
};