【摘 要】在油井措施評(píng)價(jià)與預(yù)測研究中,引入了ExtJs框架作為客戶端界面設(shè)計(jì)工具。根據(jù)項(xiàng)目需要,詳細(xì)研究了ExtJs樹形組件的源碼,對(duì)樹結(jié)點(diǎn)的兩種狀態(tài)(選中與未選中)進(jìn)行了分析與改進(jìn),擴(kuò)展實(shí)現(xiàn)了三態(tài)樹組件-——樹結(jié)點(diǎn)的三種狀態(tài)(全選,半選,未選),提高了用戶體驗(yàn)。
【關(guān)鍵詞】ExtJs;樹形組件;三態(tài)樹
1.引言
EXTJS是一個(gè)優(yōu)秀的富客戶端JS框架,提供了許多豐富華麗的客戶端組件,如表格,表單,選項(xiàng)卡面板,樹形控件等,方便了WEB應(yīng)用程序的開發(fā),提高了WEB程序的客戶體驗(yàn),尤為重要的是,該框架不僅提供了源代碼,而且提供了擴(kuò)展接口,方便用戶對(duì)現(xiàn)有的控件進(jìn)行定制擴(kuò)展。在遼河油田曙光采油廠的油井措施評(píng)價(jià)與預(yù)測系統(tǒng)中,使用到了EXTJS中的樹形控件,EXTJS自身提供的樹形控件功能比較豐富,但每個(gè)節(jié)點(diǎn)只有兩種狀態(tài)選中和非選中,但在實(shí)際情況中,當(dāng)樹形父節(jié)點(diǎn)的多個(gè)子結(jié)點(diǎn)的部分為選中,部分為非選中狀態(tài)時(shí),此時(shí)父節(jié)點(diǎn)的實(shí)際狀態(tài)為半選中狀態(tài),EXTJS原有的樹形控件并沒有處理這一種情況,本文根據(jù)項(xiàng)目實(shí)際需要,對(duì)EXTJS中的樹形控件進(jìn)行擴(kuò)展,實(shí)現(xiàn)了樹結(jié)點(diǎn)的三種狀態(tài)——即全選,半選,未選。
2.EXTJS樹形控件實(shí)現(xiàn)原理
EXTJS中,樹對(duì)象是TreePanel類型,代表樹結(jié)構(gòu)面板,它有2個(gè)重要的屬性:
root屬性:設(shè)置樹對(duì)象的根結(jié)點(diǎn)對(duì)象,為TreeNode或者AsyncTreeNode類型;
loader屬性:設(shè)置樹加載器對(duì)象,讀取遠(yuǎn)程結(jié)點(diǎn)數(shù)據(jù)(樹形結(jié)構(gòu)的JSON數(shù)據(jù)),為TreeLoader類型;
其中,TreeLoader類型也有2個(gè)重要的屬性:
dataUrl屬性:設(shè)置獲JSON數(shù)據(jù)的后臺(tái)頁面地址;
uiProvider屬性:設(shè)置樹結(jié)點(diǎn)的表現(xiàn)形式,即樣式類型,默認(rèn)為TreeNodeUI,TreeNodeUI類即EXTJS自身提供類型,提供了2種狀態(tài)的樹結(jié)點(diǎn)呈現(xiàn)方式。
由此可以看出,在EXTJS中,樹節(jié)點(diǎn)樣式與樹結(jié)點(diǎn)本身是兩個(gè)相互獨(dú)立的部分,可以通過設(shè)置樹節(jié)點(diǎn)的表現(xiàn)形式來改變樹結(jié)構(gòu)的實(shí)現(xiàn)效果,從而達(dá)到擴(kuò)展實(shí)現(xiàn)一個(gè)新的樹形組件的目的。具體須擴(kuò)展二個(gè)類:TreeLoader,TreeNodeUI,其中重點(diǎn)是擴(kuò)展TreeNodeUI類。
3.三態(tài)樹擴(kuò)展實(shí)現(xiàn)過程
3.1 TreeLoader類擴(kuò)展
對(duì)該類的擴(kuò)展比較簡單,重寫其uiProvider屬性,設(shè)置為新擴(kuò)展的TreeNodeUI類型即可。
Ext.ux.TreeThreeStateLoader=function(){
this.baseAttrs={uiProvider:Ext.ux.TreeThreeStateNodeUI};
Ext.ux.TreeThreeStateLoader.superclass.constructor.apply(this,arguments);
};
Ext.extend(Ext.ux.TreeThreeStateLoader,Ext.tree.TreeLoader);
其中,Ext.ux.TreeThreeStateLoader為新擴(kuò)展的TreeLoader類型名,Ext.extend是EXTJS提代的擴(kuò)展接口。
3.2 TreeNodeUI類擴(kuò)展
擴(kuò)展思路如下:
(1)checked屬性由’true’與’1’兩種狀態(tài)擴(kuò)展為:’all’、’part’、’none’“三態(tài)”,分別為全選,半選,非選中狀態(tài)。該屬性不需要重寫,只須在改變該屬性值的相關(guān)方法中設(shè)置其值為新的狀態(tài)值即可,但須注意擴(kuò)展后的類的JSON源數(shù)據(jù)也須由原來的二態(tài)格式:
{{id:’090100’,checked:’true’},{id:’090101’,
checked:’true’},{id:’090102’,checked:’1’}}
轉(zhuǎn)換為新的三態(tài)格式:
{{id:’090100’,checked:’part’},{id:’090101’,
checked:’all’},{id:’090102’,checked:’none’}}。
(2)新增onlyLeafCheckable屬性用于控制是否只有葉子節(jié)點(diǎn)可選,新增checkModel屬性控制單選還是多選;
(3)重寫renderElement方法,該方法須重寫新的樹結(jié)點(diǎn)呈現(xiàn)方式,以及為樹結(jié)點(diǎn)注冊單擊事件,以獲取或設(shè)置結(jié)點(diǎn)新狀態(tài);
renderElement方法重寫過程如下:
(1)設(shè)置樹結(jié)點(diǎn)的三態(tài)圖標(biāo)存放路徑以及新的CSS樣式。注意:需要準(zhǔn)備三種狀態(tài)的圖標(biāo)文件:all.gif,part.gif,none.gif。
(2)注冊單擊事件
Ext.fly(this.checkbox).on('click',this.onCheck.createDelegate(this,[1]));
//當(dāng)樹結(jié)點(diǎn)狀態(tài)發(fā)生變化時(shí),調(diào)用onCheck方法呈現(xiàn)結(jié)點(diǎn)新狀態(tài)
onCheck方法是該類擴(kuò)展的主要方法,包括獲取當(dāng)前節(jié)點(diǎn)狀態(tài),設(shè)置其子孫結(jié)點(diǎn)狀態(tài),根據(jù)當(dāng)前節(jié)點(diǎn)以及其兄弟節(jié)點(diǎn)狀態(tài)設(shè)置其父節(jié)點(diǎn)狀態(tài),代碼如下:
onCheck:function(){
var curNode=this.node;
var checkedStaus=this.toggleCheck(curNode.attributes.checked);//獲取狀態(tài)
curNode.attributes.checked=checkedState;
this.setNodeIcon(curNode);//當(dāng)前節(jié)點(diǎn)
this.childCheck(curNode,curNode.attributes.checked);//子結(jié)點(diǎn)
this.parentCheck(curNode);//父節(jié)點(diǎn)
this.onCheckChange();
}
對(duì)以上代碼的解釋如下:
(1)toggleCheck方法
重寫了父類的方法,獲取鼠標(biāo)選中節(jié)點(diǎn)的狀態(tài)值,主要代碼為:
curNode.checked=(value=='all'||value=='part')?'none':'all';
curNode代表鼠標(biāo)選中的節(jié)點(diǎn),注意,鼠標(biāo)選中的當(dāng)前節(jié)點(diǎn)只可能變化成兩種狀態(tài),選中或取消選中,而且其子孫節(jié)點(diǎn)的狀態(tài)與其相一致,其父節(jié)點(diǎn)的狀態(tài)則會(huì)依據(jù)其兄弟結(jié)點(diǎn)的狀態(tài)發(fā)生三種變化。
(2)setNodeIcon方法
設(shè)置節(jié)點(diǎn)的圖標(biāo),主要代碼為:
curNode.checkbox.src='images/'+curNode.attributes.checked+”.gif”;
curNode代表當(dāng)前結(jié)點(diǎn),src屬性代表結(jié)點(diǎn)的圖片存儲(chǔ)路徑。注意:必須將代表三種狀態(tài)的節(jié)點(diǎn)圖片all.gif,part.gif,none.gif存儲(chǔ)在以上代碼指定的路徑中。
(3)childCheck方法
設(shè)置子節(jié)點(diǎn)狀態(tài)。該方法循環(huán)遞歸設(shè)置其每一個(gè)子孫節(jié)點(diǎn)的狀態(tài),并調(diào)用setNodeIcon方法設(shè)置圖標(biāo)。主要代碼如下:
childCheck:function(curNode,checked){
if(curNode.childNodes)//判斷當(dāng)前節(jié)點(diǎn)是否為葉子節(jié)點(diǎn)
Ext.each(curNode.childNodes,function(child){//循環(huán)設(shè)置每個(gè)子結(jié)點(diǎn)
child.attributes.checked=checked;//子結(jié)點(diǎn)的狀態(tài)與當(dāng)前結(jié)點(diǎn)狀態(tài)保持一致
this.setNodeIcon(child);//設(shè)置結(jié)點(diǎn)圖標(biāo)
this.childCheck(child,checked);//遞歸判斷
},this);
},
(4)parentCheck方法
設(shè)置父節(jié)點(diǎn)狀態(tài)。該方法循環(huán)遞歸判斷其父節(jié)點(diǎn)的狀態(tài),并調(diào)用setNodeIcon方法設(shè)置圖標(biāo)。主要代碼如下:
while((curNode=curNode.parentNode)!=1){//由當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)開始,向上遞歸
var part=1;//標(biāo)識(shí)變量,是否為半選
var selNum=0;//標(biāo)識(shí)變量,記錄當(dāng)前節(jié)點(diǎn)被選中的子節(jié)點(diǎn)數(shù)
Ext.each(curNode.childNodes,function(child){//循環(huán)所有子節(jié)點(diǎn)的checked屬性
if(child.attributes.checked=='all')
selNum++;//子節(jié)點(diǎn)選中時(shí),令標(biāo)識(shí)變量自加1
else if(child.attributes.checked=='part'){
part=true;return;//子節(jié)點(diǎn)中存在半選中狀態(tài)時(shí),設(shè)置標(biāo)識(shí)變量,退出比較
}
});
if(part)//判斷半選標(biāo)識(shí)變量(下轉(zhuǎn)第17頁)(上接第6頁)
curNode.attributes.checked='part';
else{//根據(jù)全選標(biāo)識(shí)變量值與子結(jié)點(diǎn)的總個(gè)數(shù)判斷結(jié)點(diǎn)狀態(tài)
if(selNum==curNode.childNodes.length){
curNode.attributes.checked='all';
}else if(selNum==0){
curNode.attributes.checked='none';
}else{curNode.attributes.checked='part';}
}
this.setNodeIcon(curNode);//設(shè)置結(jié)點(diǎn)圖標(biāo)
};
(5)onCheckChange方法
是調(diào)用父類方法,用以觸發(fā)checkchange事件,為用戶提供處理結(jié)點(diǎn)狀態(tài)變化的接口(如獲取樹點(diǎn)的ID值或text值)。
擴(kuò)展后的三態(tài)樹類結(jié)構(gòu)如下:
Ext.ux.TreeThreeStausNodeUI=function(){//三態(tài)樹節(jié)點(diǎn)類型名
this.checkModel='multiple';//新加屬性,設(shè)置樹結(jié)點(diǎn)是否多選
this.onlyLeafCheckable=1;//新加屬性,設(shè)置是否只有葉子結(jié)點(diǎn)可以選中
Ext.ux.TreeThreeStausNodeUI.superclass.constructor.apply(this,arguments);
};
Ext.extend(Ext.ux.TreeThreeStausNodeUI,Ext.tree.TreeNodeUI,{
renderElement:function(){}
onCheck:function(){},
//toggleCheck方法,childCheck方法,parentCheck方法,setNodeIcon方法的定義
}
4.使用示例
擴(kuò)展后的三態(tài)樹的使用方法與普通樹大體相同,只須注意以下兩點(diǎn)變化:
(1)聲明TreePanel對(duì)象時(shí),指定loader屬性為新擴(kuò)展的TreeThreeStausLoader類型;
(2)在聲明TreeThreeStausLoader對(duì)象時(shí),指定的DataUrl中返回的JSON數(shù)據(jù)一定要符合新的三態(tài)數(shù)據(jù)格式。
圖1 三態(tài)樹實(shí)現(xiàn)效果
在項(xiàng)目中用到擴(kuò)展的三態(tài)樹的效果圖如圖1所示。
5.小結(jié)
本文分析了如何擴(kuò)展ExtjJs現(xiàn)有樹形組件的思路和方法,并詳細(xì)介紹了三態(tài)樹的擴(kuò)展實(shí)現(xiàn)過程,解決了實(shí)際WEB開發(fā)中樹形控件的復(fù)雜狀態(tài)顯示問題。
本文創(chuàng)新點(diǎn):通過擴(kuò)展EXTJS現(xiàn)有樹形組件實(shí)現(xiàn)了三態(tài)樹實(shí)現(xiàn)效果。
參考文獻(xiàn):
[1]朱立明,黃衛(wèi)忠,倪雄軍.ExtJS框架下樹形組件改進(jìn)及應(yīng)用[J].計(jì)算機(jī)應(yīng)用,2010,30(2):242-248.
[2]張鑫,黃燈橋,楊彥強(qiáng).JavaScript凌厲開發(fā):Ext JS 3詳解與實(shí)踐[M].北京:清華大學(xué)出版社,2010.
[3]衛(wèi)軍,夏慧軍,孟臘春.ExtJS WEB應(yīng)用程序開發(fā)指南[M].北京:機(jī)械工業(yè)出版社,2009.
[4]徐會(huì)生,何啟偉,康愛媛.深入淺出Ext JS[M].北京:人民郵電出版,2009.
[5]黃燈橋,徐會(huì)生.ExtJS高級(jí)程序設(shè)計(jì)[M].北京:機(jī)械工業(yè)出版社,2010.
[6]彭仁夔.ExtJS源碼分析與開發(fā)實(shí)例寶典[M].北京:電子工業(yè)出版社,2010.
作者簡介:瞿詩高(1984—),湖北洪湖人,長江大學(xué)工程技術(shù)學(xué)院講師,研究方向:軟件開發(fā)、MIS系統(tǒng)、數(shù)據(jù)庫應(yīng)用。