Ext中的事件機制
Ext中的事件機制是在 Ext.util.Observable 中定義的,舉一個例子來說明事件機制,先看一下代碼,然後慢慢說
person.js
- Ext.namespace("com.meizhi");
- /* 定義NameSpace的別名 */
- Mz = com.meizhi;
- Mz.Person = function() {
- /* 定義事件 */
- this.addEvents(
- "nameChange",
- "sexChange"
- );
- };
- Ext.extend(Mz.Person, Ext.util.Observable, {
- name:"",
- sex:"",
- setName:function(_name){
- if(this.name != _name) {
- /* 發佈事件 */
- this.fireEvent("nameChange", this, this.name, _name);
- this.name = _name;
- }
- },
- setSex:function(_sex){
- if(this.sex != _sex){
- /* 發佈事件 */
- this.fireEvent("sexChange", this, this.sex, _sex);
- this.sex = _sex;
- }
- }
- });
Ext.namespace("com.meizhi"); /* 定義NameSpace的別名 */ Mz = com.meizhi; Mz.Person = function() { /* 定義事件 */ this.addEvents( "nameChange", "sexChange" ); }; Ext.extend(Mz.Person, Ext.util.Observable, { name:"", sex:"", setName:function(_name){ if(this.name != _name) { /* 發佈事件 */ this.fireEvent("nameChange", this, this.name, _name); this.name = _name; } }, setSex:function(_sex){ if(this.sex != _sex){ /* 發佈事件 */ this.fireEvent("sexChange", this, this.sex, _sex); this.sex = _sex; } } });
看JS文件中的定義
- 先定義了一個Person類,在類中只有一個屬性addEvents,屬性值是一個字符串數組,在這裏是定義了兩個 Event 事件的名字。
- 聲明 Person 類繼承自 Ext.util.Observable 類,並且定義了Person類的另外一些屬性:name,sex,以及它們的寫方法(這裏把setName和setSex稱爲屬性更合適一些)。
- 在屬性的寫方法 setName ,setSex 中,如果傳入的值和實例化的 Person 對象的屬性值不一致就會調用相應的事件,並且給屬性賦值。
- 這樣 Person 類的屬性定義就完成了,並且現在 Person 是具有Ext事件機制的類,以後在 Person 類的實例中綁定和調用事件就非常方便了。
Ext.util.Observable 維護了一個events 對象的數組,並提供了更加方便的對於事件的封裝和調用機制。(參考:http://www.cnblogs.com/meetrice/archive/2008/05/23/1206108.html )
addEvents():綁定事件,看一下它的源代碼
- addEvents : function(o){
- if(!this.events){
- this.events = {};
- }
- if(typeof o == 'string'){
- for(var i = 0, a = arguments, v; v = a[i]; i++){
- if(!this.events[a[i]]){
- // 將傳入的事件名稱註冊到事件列表中
- this.events[a[i]] = true;
- }
- }
- }else{
- Ext.applyIf(this.events, o);
- }
- }
addEvents : function(o){ if(!this.events){ this.events = {}; } if(typeof o == 'string'){ for(var i = 0, a = arguments, v; v = a[i]; i++){ if(!this.events[a[i]]){ // 將傳入的事件名稱註冊到事件列表中 this.events[a[i]] = true; } } }else{ Ext.applyIf(this.events, o); } }
該方法實際上就是在 Person 對象上綁定了兩個沒有任何實現的事件名 ,這樣 Person 對象就具有了兩個空的Event對象(綁定可以執行操作的Event對象使用 addlistener 方法)。
fireEvent():發佈事件,也就是觸發綁定的事件。源代碼中的定義
- fireEvent : function(){
- if(this.eventsSuspended !== true){
- //通過addEvents()註冊的事件會被封裝成events對象
- var ce = this.events[arguments[0].toLowerCase()];
- if(typeof ce == "object"){
- //觸發事件對象
- return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
- }
- }
- return true;
- }
fireEvent : function(){ if(this.eventsSuspended !== true){ //通過addEvents()註冊的事件會被封裝成events對象 var ce = this.events[arguments[0].toLowerCase()]; if(typeof ce == "object"){ //觸發事件對象 return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1)); } } return true; }
現在Person類的結構就很清楚了,
- name屬性
- sex屬性
- setName屬性(如果傳入參數和name屬性不一致,調用 “nameChange” 事件)
- setSex屬性(如果傳入參數和name屬性不一致,調用 “sexChange” 事件)
在person.js中完成是事件的定義和發佈,那事件是在什麼時候被訂閱的呢? 事件觸發之後又要進行哪些操作呢?
person.html
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <title>Event</title>
- <link type="text/css" rel="stylesheet" href="../../ext/resources/css/ext-all.css">
- <script type="text/javascript" src="../../ext/adapter/ext/ext-base.js"></script>
- <script type="text/javascript" src="../../ext/ext-all.js"></script>
- <script type="text/javascript" src="person.js"></script>
- <script type="text/javascript">
- var _person = null;
- button_click = function() {
- _person.setName(prompt("請輸入姓名", ""));
- _person.setSex(prompt("請輸入性別", ""));
- }
- Ext.onReady(function(){
- var txt_name = Ext.get("txt_name");
- var txt_sex = Ext.get("txt_sex");
- /* 構建Person類 */
- _person = new Mz.Person();
- /* 訂閱事件 */
- _person.on("nameChange",
- function(_person, _old, _new){
- txt_name.dom.value = _new;
- });
- /* 訂閱事件 */
- _person.on("sexChange",
- function(_person, _old, _new){
- txt_sex.dom.value = _new;
- });
- /* 訂閱事件 */
- _person.on("nameChange",
- function(_person, _old, _new){
- document.title = _new;
- });
- });
- </script>
- </head>
- <body>
- 姓名:<input type="text" id="txt_name" maxlength="10" /><br/>
- 性別:<input type="text" id="txt_sex" maxlength="10" /><br/>
- <input type="button" value="輸入" onclick="button_click()"/>
- </body>
- </html>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Event</title>
<link type="text/css" rel="stylesheet" href="../../ext/resources/css/ext-all.css">
<script type="text/javascript" src="../../ext/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../../ext/ext-all.js"></script>
<script type="text/javascript" src="person.js"></script>
<script type="text/javascript">
var _person = null;
button_click = function() {
_person.setName(prompt("請輸入姓名", ""));
_person.setSex(prompt("請輸入性別", ""));
}
Ext.onReady(function(){
var txt_name = Ext.get("txt_name");
var txt_sex = Ext.get("txt_sex");
/* 構建Person類 */
_person = new Mz.Person();
/* 訂閱事件 */
_person.on("nameChange",
function(_person, _old, _new){
txt_name.dom.value = _new;
});
/* 訂閱事件 */
_person.on("sexChange",
function(_person, _old, _new){
txt_sex.dom.value = _new;
});
/* 訂閱事件 */
_person.on("nameChange",
function(_person, _old, _new){
document.title = _new;
});
});
</script>
</head>
<body>
姓名:<input type="text" id="txt_name" maxlength="10" /><br/>
性別:<input type="text" id="txt_sex" maxlength="10" /><br/>
<input type="button" value="輸入" onclick="button_click()"/>
</body>
</html>
HTML文件頁面中定義了兩個輸入框,和一個按鈕,通過Ext.onReady(),頁面初始化後首先執行裏面的代碼
- Ext.get()取得文本框中的值
- 構造 Person 類實例
- 訂閱事件,這個時候定義 Person 中的設值屬性的操作具體執行的內容,給setName()和setSex()訂閱的事件中添加內容,這一點非常靈活,我們一開始只要先定義一個事件,而這個事件只有一個名字,沒有具體實現,我們在訂閱這個事件的時候才告訴它如果事件發生我們需要進行什麼樣的操作。
在點擊按鈕的時候,調用 button_click 方法來給 person 實例屬性賦值。
在訂閱事件的時候,用到的是 Observable.on 方法,實際上它是 Observable.addListener 的縮寫(Ext裏面的縮寫好多啊...),源代碼中是這樣定義的
- Ext.util.Observable.prototype.on = Ext.util.Observable.prototype.addListener;
Ext.util.Observable.prototype.on = Ext.util.Observable.prototype.addListener;
綁定事件真正起作用的方法就是addListener()方法,源碼中的定義
- addListener : function(fn, scope, options){
- scope = scope || this.obj;
- if(!this.isListening(fn, scope)){
- var l = this.createListener(fn, scope, options);
- if(!this.firing){
- this.listeners.push(l);
- }else{ // if we are currently firing this event, don't disturb the listener loop
- this.listeners = this.listeners.slice(0);
- this.listeners.push(l);
- }
- }
- }
addListener : function(fn, scope, options){ scope = scope || this.obj; if(!this.isListening(fn, scope)){ var l = this.createListener(fn, scope, options); if(!this.firing){ this.listeners.push(l); }else{ // if we are currently firing this event, don't disturb the listener loop this.listeners = this.listeners.slice(0); this.listeners.push(l); } } }
最後看一下效果:
- 頁面中有兩個input框和一個button
- 點擊“輸入”button,然後彈出來一個對話框,要求輸入姓名
- 點擊確定,輸入框的內容會自動賦值到 id 爲 txt_name 的 input 框中,HTML文檔的標題也會變成剛纔輸入的姓名的值,同時會彈出第二個對話框要求輸入性別
- 輸入性別確認,值被帶到 id 爲 txt_sex 的 input 框中
順便說一下例子中涉及到的其他的方法:
- Ext.onReady():用法相當於 window.onload ,是在頁面裝載完成時調用的方法,Ext.onReady是一個簡寫,定義在Ext.core.EventManager中,源代碼:
- onDocumentReady : function(fn, scope, options){
- if(!docReadyEvent){
- initDocReady();
- }
- if(docReadyState || Ext.isReady){ // if it already fired
- options || (options = {});
- fn.defer(options.delay||0, scope);
- }else{
- docReadyEvent.addListener(fn, scope, options);
- }
- }
onDocumentReady : function(fn, scope, options){ if(!docReadyEvent){ initDocReady(); } if(docReadyState || Ext.isReady){ // if it already fired options || (options = {}); fn.defer(options.delay||0, scope); }else{ docReadyEvent.addListener(fn, scope, options); } }
- Ext.get()方法,它用來取得頁面中的一個DOM對象。(參考:http://www.vifir.com/bbs/html/20080706/1703993.html )
- txt_name.dom.value, 用來給DOM對象賦值,相當於document.getElementById("txt_name").value
在這個例子中,我們訂閱了兩次 nameChange 事件,這也是Ext中很重要的一個機制 -- 事件列表 ,在整個應用當中,公佈一個事件,這個事件可以被多次訂閱,這一點是很常用的,實現了一個完善的應用體系。
通過上面的例子也可以看出Ext的一些缺點:完成這樣一個 事件定義、發佈、過程所用到的代碼比較複雜,不夠簡潔,要是更加直觀一些就更好了,可以讓人更加容易理解一些。