感謝reedseutozte的投稿,一篇關於dijit.Tree的使用方法介紹,非常好的文章,也很能體現出Dojo的核心設計思想。作為最常用的一個控制項,相信這篇文章能協助到很多同學。再次感謝reedseutozte,為Dojo中文部落格帶來了第一篇投稿文章:)
概述
Dojo的dijit.tree的代碼結構完全遵循MVC結構,結構非常嚴謹:
M:model使用了dojo基礎包提供的dojo.data.itemFileReadStore(唯讀)或者dojo.data.itemFileWriteStore(可讀寫)。Tree並不直接使用Store而是通過dijit.tree.TreeStoreModel這個類將sotre和樹形結構所需要的結構的進行串接。
V:view就是tree.js中定義的dijit.tree和dijit_TreeNode。這個類主要完成前台的介面渲染,以及樹上節點對象之間的管理。
C:_dndSelector.js這個檔案名稱開始迷惑了我,後來才發現即使不使用拖拽特性,該類中的代碼依然會被調用,這個類定義選中節點,刪除節點,增加節點,托拽節點的操作。
網上關於tree 的資料根多,而且dojo自身也提供了豐富的範例,這裡介紹一些其範例中沒有涉及的一些用法。
樹的懶載入(lazy load)
其實懶載入本身沒什麼太多的問題,只是有兩點需要說明
表徵圖處理
Dojo對於葉子/非葉子節點的表徵圖處理時會做自動判斷。在懶載入時由於有些節點沒有載入子節點,dojo在處理這些節點的表徵圖的時候會顯示預設的葉子表徵圖,所以這裡需要重 新實現treemodel的mayHaveChildren方法,代碼如下
var treeModel = new dijit.tree.ForestMode({ ....... }); treeModel.mayHaveChildren = function(item) { //item為對應節點的資料項目,該函數返回true表示該節點為非葉子節點,dojo就會為這個節點附著上非葉子表徵圖 if(item.root) { return true; } else { //這裡認為初始化樹的時候對於每一個節點的資料項目中都有type屬性,根據屬性判斷 return (treeModel.store.getValue(item, 'type') != 'xxxx') } }
節點載入
Dojo樹最大的特點就是完全可以靠資料驅動,因此節點的載入,完全可以通過往對應的 父節點的資料項目中增加childran實現,參見如如下代碼:
var oldExpand = dijit.Tree.prototype._onExpandoClick; dojo.extend(dijit.Tree, { _onExpandoClick: function(message) { var node = message.node; reloadNode(node); oldExpand.apply(this, arguments); } }); function reloadNode(node) { var store = node.tree.model.store; var nodeItem = node.item; //假設我們通過這個方法從後台取得了改node的子節點資料 var dataList = getChildList(); dojo.forEach(dataList, function(x){ ...... x.type = 'yyy'; //以上的代碼可以看需要添加,上面一行代碼的作用是可以通過store.getValue方法訪問該節點對應的item的type屬性 store.newItem(x,{parent:nodeItem, attribute: 'children'}); }); }
增加/刪除/修改節點
之前已經說過dojo的tree採用了MVC模式,因此上述操作完全可以通過對store的操作實現
1. 增加
上面的懶載入代碼已經列出這裡不再贅述
2. 修改:
假設需要修改的節點的treeNode對象執行個體為node
tree.model.store.setValue(node.item, 'name'/*取決於定義store時的label屬性*/, newName)
3. 刪除
假設需要刪除的節點的treeNode對象執行個體為node
tree.model.store.deleteItem(node.item)
滑鼠函數
上面說了很多,可以發現所有操作都需要擷取樹節點對應的dojo對象才可以進行。那麼這個對象如何獲得呢?我們知道對樹的操作分為兩種,左鍵點擊或者右鍵菜單,滑鼠點中了相對應的節點就可繼續往下操作,因此下面介紹如何通過滑鼠事件獲得節點對象
click
tree.connect(tree, 'onClick', clickTreeNode); function clickTreeNode(item/*點中節點對應的資料項目*/, node/*點中的對象,這裡node.item就是的第一個參數*/,evt/*事件*/) { ................ }
rightClick
右擊一般是開啟菜單,這裡的菜單也是dojo的,因此判斷函數為
dojo.connect(pMenu, '_openMyself', function(e){ var node = dijit.getEnclosingWidget(e.target); /*node就是節點對象*/ });
代碼選中節點
tree.dndController.setSelection([node])
托拽控制
Dojo的樹提供托拽功能,使用托拽功能需要在執行個體化tree的時候將tree的dndController屬性定義為’dijit.tree.dndSource' 。betweenThreshold為0表示不允許同目錄下拖動。
實際應用中我們要增加一些限制,這坐介紹兩種
i)選中的對象是否允許拖拽
dojo.connect(tree.dndController, 'onMouseDown', function(e){ //如果你的樹上有捲軸,請加入如下代碼,否則如果你選中了節點後拖動捲軸會出現節點拖拽精靈 if (dijit.getEnclosingWidget(e.target) == tree) { tree.dndController.mousedown = false; return; } //tree.dndController.mousedown為true表示允許拖拽,反之就是不允許,e為滑鼠事件對象 //tree的selectedNodes屬性可以返回選中的節點列表,這裡的代碼錶示每個選中節點的資料項目中的type屬性都是xxx才能拖動 tree.dndController.mousedown = dojo.every(tree.selectedNodes, function(node){ return (tree.model.store.getValue(node.item, 'type') == 'xxx') }); });
ii)判斷目標節點是否接受正在拖拽的對象
tree.checkItemAcceptance = function(target, source, position) { //target: DomNode 目標節點對應的dom, 用dijit.getEnclosingWidget(target)可以獲得TreeNode對象 //source: dijit.tree.dndSource 被拖動的treeNode對象,是一個列表,因為tree允許一次拖動多個節點 //position: 'over', 'before', 'after' //返回true表示允許drop }