ExtJS类作用域问题:组件销毁指向错误
这个问题的核心是作用域绑定错误:你在各个回调里用var me = this,但这些回调的scope被设置为Grid面板(比如Ext.MessageBox.confirm的最后一个参数是me,也就是Grid),所以this始终指向Grid,调用me.destroy()自然会删掉Grid本身,而不是目标组件。
下面分两个场景给出具体的修复方案:
1. 修复Window的Cancel按钮销毁错误
当你点击"Yes"创建Window后,Cancel按钮的回调里需要获取当前Window实例,而不是Grid。有两种简单的方式:
方式一:直接引用Window实例
创建Window时把它赋值给一个变量,然后在Cancel按钮的点击回调里调用这个变量的destroy():
if (button === "yes") { // 把Window实例存到变量里 var updateWin = new Ext.window.Window({ alias: 'updateWin', autoShow: true, title: translations.update, modal: true, width: 350, height: 200, items: [ // ... 你的items代码不变 ], dockedItems: [ { xtype: 'toolbar', dock: 'bottom', items: [ { xtype: 'tbfill' }, { xtype: 'button', text: translations.cancel, listeners: { click: function () { // 销毁当前Window实例,而不是Grid updateWin.destroy(); } } }, // ... 保存按钮代码不变 ] } ] }); return updateWin; }
方式二:通过组件向上查找
利用ExtJS的组件查询方法up(),从按钮本身向上找到父级Window:
{ xtype: 'button', text: translations.cancel, listeners: { click: function (btn) { // 从按钮向上查找最近的Window组件并销毁 btn.up('window').destroy(); } } }
这种方式更灵活,不需要额外保存Window变量,尤其适合动态创建组件的场景。
2. 修复MessageBox的No按钮销毁错误
Ext.MessageBox是一个单例组件,你不需要(也不应该)销毁它,通常用hide()方法隐藏即可。如果一定要销毁,直接调用Ext.MessageBox.destroy(),而不是用me(因为me是Grid):
} else { console.log('this is no!'); // 隐藏或销毁MessageBox,而不是Grid Ext.MessageBox.hide(); // 如果确实需要销毁:Ext.MessageBox.destroy(); }
另外,这里还有一个小优化点:你在change函数里调用me.onUpdate()会再次触发选择模型的获取,还可能重复弹出MessageBox。其实可以把选中的记录作为参数传递给change函数,避免重复执行逻辑:
onUpdate: function () { var me = this; var gridRec = me.getSelectionModel().getSelection(); // 把选中的记录作为参数传给change函数 Ext.MessageBox.confirm(translations.confirm, translations.confirmChange, function(button) { me.change(button, gridRec); }, me); return gridRec; }, // 修改change函数,接收选中的记录参数 change: function (button, selectedRec) { var me = this; // 不需要再调用me.onUpdate() var selectedRecEmail = selectedRec[0].data.email; // ... 后续逻辑不变 }
完整修改后的代码片段
Ext.define('MyApp.FooGrid', { extend: 'Ext.grid.Panel', reference: 'fooGrid', getGridMenu: function () { var me = this; var ret = [ { text: 'Update', listeners: { click: me.onUpdate, scope: me } } ]; return me.callParent().concat(ret); }, onUpdate: function () { var me = this; var gridRec = me.getSelectionModel().getSelection(); // 传递选中记录到change回调 Ext.MessageBox.confirm(translations.confirm, translations.confirmChange, function(button) { me.change(button, gridRec); }, me); return gridRec; }, change: function (button, selectedRec) { var me = this; var selectedRecEmail = selectedRec[0].data.email; if (button === "yes") { return new Ext.window.Window({ alias: 'updateWin', autoShow: true, title: translations.update, modal: true, width: 350, height: 200, items: [ { xtype: 'container', height: 10 }, { xtype: 'textfield', width: 300, readOnly: true, value: selectedRecEmail }, { xtype: 'textfield', width: 300, fieldLabel: translations.newPassword } ], dockedItems: [ { xtype: 'toolbar', dock: 'bottom', items: [ { xtype: 'tbfill' }, { xtype: 'button', text: translations.cancel, listeners: { click: function (btn) { btn.up('window').destroy(); } } }, { xtype: 'button', text: translations.save, listeners: { click: function () { console.log("I'll save you!"); } } } ] } ] }); } else { console.log('this is no!'); Ext.MessageBox.hide(); } } });
总结一下,关键是要明确每个回调函数里this的指向,不要盲目用me = this,而是通过组件引用或者查询方法获取目标组件,这样就能避免误销毁Grid面板的问题啦。
内容的提问来源于stack exchange,提问作者Nuri Engin

