我最近开始学习 ExtJS,但无法理解如何处理事件。我没有任何以前版本的 ExtJS 的经验。
通过阅读各种手册、指南和文档页面,我已经弄清楚了如何使用它,但我不清楚它是如何工作的。我为旧版本的 ExtJS 找到了几个教程,但我不确定它们在 ExtJS 4 中的适用性。
我特别关注诸如此类的“最终决定”
事件处理函数传递了哪些参数?是否有一组总是通过的标准参数?
如何为我们编写的自定义组件定义自定义事件?我们如何触发这些自定义事件?
返回值(真/假)是否会影响事件的冒泡方式?如果不是,我们如何从事件处理程序内部或外部控制事件冒泡?
有注册事件监听器的标准方法吗? (到目前为止,我遇到了两种不同的方法,我不确定为什么要使用每种方法)。
例如,this question 让我相信事件处理程序可以接收很多参数。我看过其他教程,其中处理程序只有两个参数。有什么变化?
让我们从描述 DOM 元素的事件处理开始。
DOM 节点事件处理
首先,您不想直接使用 DOM 节点。相反,您可能希望使用 Ext.Element
接口。为了分配事件处理程序,创建了 Element.addListener
和 Element.on
(它们是等效的)。因此,例如,如果我们有 html:
<div id="test_node"></div>
我们想要添加 click
事件处理程序。
让我们检索 Element
:
var el = Ext.get('test_node');
现在让我们检查一下文档中的 click
事件。它的处理程序可能有三个参数:
点击(Ext.EventObject e,HTMLElement t,对象 eOpts)
知道了所有这些东西,我们可以分配处理程序:
// event name event handler
el.on( 'click' , function(e, t, eOpts){
// handling event here
});
小部件事件处理
小部件事件处理与 DOM 节点事件处理非常相似。
首先,widgets事件处理是利用Ext.util.Observable
mixin来实现的。为了正确处理事件,您的小部件必须包含 Ext.util.Observable
作为 mixin。默认情况下,所有内置小部件(如 Panel、Form、Tree、Grid 等)都将 Ext.util.Observable
作为 mixin。
对于小部件,有两种分配处理程序的方法。第一个 - 是使用 on 方法(或 addListener
)。例如,让我们创建 Button
小部件并将 click
事件分配给它。首先,您应该检查事件的文档以获取处理程序的参数:
单击(Ext.button.Button this,事件 e,对象 eOpts)
现在让我们使用 on
:
var myButton = Ext.create('Ext.button.Button', {
text: 'Test button'
});
myButton.on('click', function(btn, e, eOpts) {
// event handling here
console.log(btn, e, eOpts);
});
第二种方法是使用小部件的 listeners 配置:
var myButton = Ext.create('Ext.button.Button', {
text: 'Test button',
listeners : {
click: function(btn, e, eOpts) {
// event handling here
console.log(btn, e, eOpts);
}
}
});
请注意,Button
小部件是一种特殊的小部件。点击事件可以通过使用 handler
配置分配给这个小部件:
var myButton = Ext.create('Ext.button.Button', {
text: 'Test button',
handler : function(btn, e, eOpts) {
// event handling here
console.log(btn, e, eOpts);
}
});
自定义事件触发
首先,您需要使用 addEvents 方法注册一个事件:
myButton.addEvents('myspecialevent1', 'myspecialevent2', 'myspecialevent3', /* ... */);
使用 addEvents
方法是可选的。正如对该方法的评论所说,不需要使用此方法,但它为事件文档提供了位置。
要触发您的事件,请使用 fireEvent 方法:
myButton.fireEvent('myspecialevent1', arg1, arg2, arg3, /* ... */);
arg1, arg2, arg3, /* ... */
将被传递到处理程序。现在我们可以处理您的事件:
myButton.on('myspecialevent1', function(arg1, arg2, arg3, /* ... */) {
// event handling here
console.log(arg1, arg2, arg3, /* ... */);
});
值得一提的是,在定义新小部件时,插入 addEvents 方法调用的最佳位置是小部件的 initComponent
方法:
Ext.define('MyCustomButton', {
extend: 'Ext.button.Button',
// ... other configs,
initComponent: function(){
this.addEvents('myspecialevent1', 'myspecialevent2', 'myspecialevent3', /* ... */);
// ...
this.callParent(arguments);
}
});
var myButton = Ext.create('MyCustomButton', { /* configs */ });
防止事件冒泡
为防止冒泡,您可以使用 return false
或使用 Ext.EventObject.preventDefault()
。为了防止浏览器的默认操作使用 Ext.EventObject.stopPropagation()
。
例如,让我们将点击事件处理程序分配给我们的按钮。如果未单击左按钮,则阻止默认浏览器操作:
myButton.on('click', function(btn, e){
if (e.button !== 0)
e.preventDefault();
});
触发应用程序范围的事件
如何让控制器相互交谈...
除了上面非常棒的答案之外,我还想提一下应用程序范围的事件,这些事件在 MVC 设置中非常有用,可以实现控制器之间的通信。 (extjs4.1)
假设我们有一个带有选择框的控制器站(Sencha MVC 示例):
Ext.define('Pandora.controller.Station', {
extend: 'Ext.app.Controller',
...
init: function() {
this.control({
'stationslist': {
selectionchange: this.onStationSelect
},
...
});
},
...
onStationSelect: function(selModel, selection) {
this.application.fireEvent('stationstart', selection[0]);
},
...
});
当Select框触发更改事件时,将触发函数onStationSelect
。
在该函数中,我们看到:
this.application.fireEvent('stationstart', selection[0]);
这将创建并触发一个应用程序范围的事件,我们可以从任何其他控制器监听该事件。
因此,在另一个控制器中,我们现在可以知道站选择框何时更改。这是通过收听 this.application.on
完成的,如下所示:
Ext.define('Pandora.controller.Song', {
extend: 'Ext.app.Controller',
...
init: function() {
this.control({
'recentlyplayedscroller': {
selectionchange: this.onSongSelect
}
});
// Listen for an application wide event
this.application.on({
stationstart: this.onStationStart,
scope: this
});
},
....
onStationStart: function(station) {
console.info('I called to inform you that the Station controller select box just has been changed');
console.info('Now what do you want to do next?');
},
}
如果选择框已更改,我们现在也触发控制器 Song
中的函数 onStationStart
...
来自 Sencha 文档:
应用程序事件对于具有许多控制器的事件非常有用。不是在每个控制器中监听相同的视图事件,而是只有一个控制器监听视图事件并触发其他可以监听的应用程序范围的事件。这也允许控制器在不知道或依赖于彼此的存在的情况下相互通信。
在我的例子中:单击树节点以更新网格面板中的数据。
感谢@gm2008 从以下评论更新2016:
在触发应用范围的自定义事件方面,ExtJS V5.1 发布后现在有一个新方法,即使用 Ext.GlobalEvents
。
当您触发事件时,您可以调用:Ext.GlobalEvents.fireEvent('custom_event');
当您注册事件的处理程序时,您调用:Ext.GlobalEvents.on('custom_event', function(arguments){/* handler codes*/}, scope);
此方法不限于控制器。任何组件都可以通过将组件对象作为输入参数范围来处理自定义事件。
Found in Sencha Docs: MVC Part 2
Ext.GlobalEvents.fireEvent('custom_event');
当您注册事件的处理程序时,您调用 Ext.GlobalEvents.on('custom_event', function(arguments){/* handler codes*/}, scope};
这是一个 fiddle。此方法不限于控制器。任何组件都可以通过将组件对象作为输入参数 scope
来处理自定义事件。应该重新提出这个问题。
控制器事件侦听器的另一个技巧。
您可以使用通配符来监视来自任何组件的事件:
this.control({
'*':{
myCustomEvent: this.doSomething
}
});
只是想在上面的优秀答案中添加几便士:如果您正在使用 Extjs 4.1 之前的版本,并且没有应用程序范围的事件但需要它们,我一直在使用一种非常简单的技术可能会有所帮助:创建一个扩展 Observable 的简单对象,并在其中定义您可能需要的任何应用程序范围的事件。然后,您可以从应用程序中的任何位置触发这些事件,包括实际的 html dom 元素,并通过从该组件中继所需的元素,从任何组件监听它们。
Ext.define('Lib.MessageBus', {
extend: 'Ext.util.Observable',
constructor: function() {
this.addEvents(
/*
* describe the event
*/
"eventname"
);
this.callParent(arguments);
}
});
然后,您可以从任何其他组件:
this.relayEvents(MesageBus, ['event1', 'event2'])
并从任何组件或 dom 元素中触发它们:
MessageBus.fireEvent('event1', somearg);
<input type="button onclick="MessageBus.fireEvent('event2', 'somearg')">
还有两件事我发现对了解很有帮助,即使它们不是问题的一部分,真的。
您可以使用 relayEvents
方法告诉组件侦听另一个组件的某些事件,然后再次触发它们,就好像它们来自第一个组件一样。 API 文档给出了中继 store load
事件的网格示例。在编写封装多个子组件的自定义组件时非常方便。
反过来,即将封装组件 mycmp
接收到的事件传递给它的一个子组件 subcmp
,可以像这样完成
mycmp.on('show' function (mycmp, eOpts)
{
mycmp.subcmp.fireEvent('show', mycmp.subcmp, eOpts);
});