ExtJS——觀察者及被觀察者

觀察者:

觀察的主體是用來進行檢測部分的功能,它是在類庫中實現的共有功能。對於採用觀察者模式事件來講,首先我們定義被觀察者的接口,比如是要進行擴展,就是實現什麼的接口或按什麼格式的來編寫註冊的功能。Ext組件的事件機制採用是的規定格式,這個規定是格式是規定函數名和函數的參數形式。

對於這個規定,Observable類給出一個addEvent方法用來每個需要事件能力組件在初始化時就進行格式規定。

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]]){  o[a[i]] = true; } }}
else{Ext.applyIf(this.events, o); }
},
這個方法是爲了讓組件在實現自己的事件能力時規定它的事件函數的格式。它有兩種應用形式,如下:

形式一:addEvents(''click'',''dblclick'',''change'')

形式二:addEvents({click:true,dblclick: dblclickEvent})

第一種每個參數都是字符形的事件名,第二種形式是採用JSON對象把所有的事件和其事件對象對應起來。它通過繼承方法成爲每個實例化的組件的方法,也就是說它把所有的事件都存放在當前組件的events中。但是無論採用形式一還是採用形式二。其生成的事件對象都只能說是一個僞對象。因爲默認的方式這個事件對象採用true來標識。形式二的第二種用法是一般都不會用的,因爲在開發時,組件實例的很多事件都不會被使用。而在生成組件實例的時候就爲其每個事件都生成事件對象是不值得的。這樣會佔用內存和影響效率。它這種把組件對象的實現推遲到addListener中創建是有一定考慮的。

在這裏我們只看到了事件名,但是我們並不沒有看到如何去規定事件註冊函數的格式,這個在組件初始化中只是通過註釋的方式來進行說明,如在Component類initComponet方法中就有如下

this.addEvents( /** @event disable    
* Fires after the component is disabled.
                    * @param {Ext.Component} this  */
         ''disable'',.. ..}
這樣的註釋就是用來規定函數的參數。如這個disable的事件只有一個參數就是當前組件,這個參數是組件在調用這個事件回調函數時傳入的,開發者在編寫事件回調函數可以使用這個參數。這個和瀏覽器中事件監聽函數中參數event是一樣的意思。其實對於這個的事件名,也可以不用通過addEvents聲明,可以通過註釋來聲明,在addListener函數中如果沒有找到對應的事件名就會自動創建該事件名的。

對於每個事件,它的監聽函數的格式(即參數)是通過組件那些需要擴展的地方來實現的。在組件中,如果什麼地方需要開發進行它的私有功能的擴展,那麼就在這個地方執行開發者註冊的事件函數。

這個調用就是根據上面指定的事件名來找到其對應的監聽函數(可能有多個)。組件要判斷這個事件是否註冊了監聽函數和如何找到它們就是通過Observable類中的fireEvent方法:

fireEvent : function(){
 if(this.eventsSuspended !== true){    //支持全局的掛起事件
  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用在組件需要擴展的地方進行檢測是否註冊事件監聽及運行它們。我們已經看出它的一個參數是事件名,先通過這個事件名在當前組件註冊的事件中找其對應的事件對象起來,一般來說都可以找到,因爲在使用fireEvent時是根據規定事件名來指定的。如果找到該事件名對應是對象,那麼就通過事件對象內部的fire方法來執行它。在默認的時候,它的事件對象都是 boolean類型的true。如果其是object類型,那麼就是在開發通過註冊方法進行了事件監聽函數註冊。

我們看一下①處的傳入參數的方式,它會把fireEvent所有的參數除去第一個事件名的參數傳入到事件對象中的所有監聽函數中去。也就是組件在使用fireEvent時候給定的參數就是爲了給事件的監聽函數規定其參數格式。這個體現在addEvents中就是採用註釋的方式來說明。

我們對應着上面的''disable''的事件名,而component類中的disable方法中就有着如下的代碼:this.fireEvent("disable", this);它是用來調用執行開發者註冊的事件監聽,也就是觀察者模式的監測點。如果組件指定類,那麼在其類的代碼中就是對應於建立這樣的監測點,用來調用執行事件監聽函數。

對於組件的事件能力,也就只需要這兩個步驟。我們在編寫組件時,如果想讓它擁有事件能力,那麼首先得繼承於Observable類,之後,再按這二步來進行註冊事件名和建立監測點。在第二章的leftMenu類中就是這樣採用的。

 

對於觀察的主體,它還提供一個其它的方法來進行相關的操作,比如我要暫定事件或重定啓動事件。它提供如下三個方法:
方法名
 作用及使用說明
 
方法名:suspendEvents
 作用及使用說明:它是用來暫定該組件的事件能力。通過改變代碼(3.19中)this.eventsSuspended屬性值爲true。
 
方法名:resumeEvents
 作用及使用說明:對於暫定事件能力的組件,重新啓動它的事件能力,通過改變代碼this.eventsSuspended屬性值爲false.
 
方法名:relayEvents

 作用及使用說明:它的格式如下:relayEvents(o,events),它是用來把當前組件中的事件對象逐個地轉移註冊到o參數指定的對象(組件或元素等)上去。它們對應的關係就是events參數集合中指定的事件名。這就要求o對象和當前組件都有着相關的事件名。在gridPanel中就有應用。

 

 

被觀察者 

被觀察者就是開發者所編寫的事件監聽函數。對於這些監聽函數,我們得按指定的格式來編寫。這個格式是對於每個事件都是不同的。那麼編寫完成之後,我們就得把它們註冊到相對應的事件對象中去。好讓組件能監測到它,並在需要的時候去執行它來完成私有功能。

這個註冊動作就是由Observable類的addListener完成:
addListener : function(eventName, fn, scope, o){
 if(typeof eventName == "object"){
  o = eventName;
  for(var e in o){
    if(this.filterOptRe.test(e)){ continue; }   ①
    if(typeof o[e]=="function"){this.addListener(e,o[e],o.scope,o);}②
else{this.addListener(e, o[e].fn, o[e].scope, o[e]);s}            ③
   }
return;
 }
o = (!o || typeof o == "boolean") ? {} : o;                            ④      
eventName = eventName.toLowerCase();
var ce = this.events[eventName] || true;
if(typeof ce == "boolean"){ ce = new Ext.util.Event(this, eventName);
                                  this.events[eventName] = ce; }          ⑤
ce.addListener(fn, scope, o);                                             ⑥
},
開發就是通過它來實現向該組件指定的事件中註冊監聽函數。和元素的事件註冊的使用方法是一模一樣的,它也有三個方式的註冊形式,不同的它的o中只能解析四個配置項,這個通過①處的filterOptRe正則表達式/^(?:scope|delay|buffer|single)$/就可以看出來它只支持scope|delay|buffer|single四個配置項,它的含義和元素事件是也相同。

和元素事件一樣,它也一個縮寫的方法名on。它的代碼的層次也是和元素的事件註冊方法相同,不同的地方在②③處是採用遞歸調用當前函數來實現註冊多個監聽函數。其註冊單個監聽函數代碼部分在④到⑥處。在⑤處,我們可以看出在addEvents中沒有生成事件對象推遲到這裏實現。同時沒有指定事件名,也會在這裏生成與事件相對應的事件對象。

被觀察者最主要的的任務就是編寫並註冊該監聽函數。這個註冊就得是開發者在具體使用該組件中而進行註冊用的。

當我們不需要事件的監聽時,我們就要把它從事件去剔除出去。這個我們可以通過removeListener和purgeListeners方法來實現,它們不同的地方在於removeListener清除指定事件名及函數單個剔除(當然只指定事件名,就會清除該事件名所有監聽函數),而purgeListeners是清除該組件所有事件的監聽函數。
listeners: {
              selectionchange: {
                 fn: function(dv,nodes){
                     var l = nodes.length;
                     var s = l != 1 ? ''s'' : '''';
                     panel.setTitle(''Simple DataView (''+l+'' item''+s+'' selected)'');
                 }
              }
            }   

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章