近期对Ext树的使用比较多,把用到的技巧整理一下,主要分两部分:树的基本用法、拖拽时的几个难点。
一、基本用法
实现了如下功能点:右键功能、添加子节点、添加兄弟节点、树内的拖拽、叶子节点不可append问题
Ext.onReady(function() {
Ext.QuickTips.init();// tip信息
Ext.BLANK_IMAGE_URL = "lib/extjs/resources/images/default/s.gif";
Ext.form.Field.prototype.msgTarget = 'side';// 控件错误提示信息位置
var treeLoader = new Ext.tree.TreeLoader({
dataUrl : 'tree.tbdo?cmd=loadData&pid=1'
});
var rootNode = new Ext.tree.AsyncTreeNode({
id : '1',
text : 'Root'
});
var tree = new Ext.tree.TreePanel({
renderTo : 'treecontainer',
loader : treeLoader,
root : rootNode,
enableDD : true
});
var contextMenu = new Ext.menu.Menu({
items : [{
text : '添加子节点',
handler : addHandler
}, {
text : '添加兄弟点',
handler : addBrotherHandler
}, {
text : '删除',
handler : deleteHandler
}, {
text : '重命名',
handler : modifyHandler
}, {
text : '查看',
handler : viewHandler
}]
});
var treeSorter = new Ext.tree.TreeSorter(tree, {
folderSort : true,
dir : 'asc'
});
var treeEditor = new Ext.tree.TreeEditor(tree, {
allowBlank : false,
cancelOnEsc : true
});
// 弹出窗口
var win = new Ext.Window({
maskDisabled : false,
id : 'tree-window',
modal : true,// 是否为模式窗口
constrain : true,// 窗口只能在viewport指定的范围
closable : true,// 窗口是否可以关闭
closeAction : 'hide',
layout : 'fit',
width : 300,
height : 200,
plain : true,
items : [{
id : 'tree-window-view',
border : false
}]
});
tree.on('contextmenu', treeContextHandler);
// 用于展开节目时获取数据
tree.on('beforeload', function(node) {
tree.loader.dataUrl = 'tree.tbdo?cmd=loadData&pid=' + node.id; // 定义子节点的Loader
});
// 拖拽后
tree.on('beforemovenode',
function(tree, node, oldParent, newParent, index) {
// alert(tree + '-' + node + '-' + oldParent + '-' + newParent + '-' + index);
//TODO 存储入库的操作
});
// 拖拽判断,用于处理叶节目不能append的问题
tree.on('nodedragover', function(e) {
var n = e.target;
if (n.leaf) {
n.leaf = false;
}
return true;
});
treeEditor.on('beforecomplete', function(editor, newValue, oldValue) {
});
rootNode.expand(false, true);
function treeContextHandler(node, event) {
event.preventDefault();// 这行是必须的,使用preventDefault方法可防止浏览器的默认事件操作发生
node.select();
contextMenu.show(node.ui.getAnchor());
}
// 插入子节点
function addHandler() {
var newNode = new Ext.tree.TreeNode({
text : '新建节点'
});
var selectedNode = tree.getSelectionModel().getSelectedNode();
selectedNode.leaf = false;
selectedNode.expand(false, true, function() {
// 注意,应该先由expand展开非叶子节点,才能为之插入子节点,否则会出错
selectedNode.appendChild(newNode);
tree.getSelectionModel().select(newNode);
setTimeout(function() {
treeEditor.editNode = newNode;
treeEditor.startEdit(newNode.ui.textNode);
}, 10);
});
}
function addBrotherHandler() {
var newNode = new Ext.tree.TreeNode({
text : '新建节点'
});
var selectedNode = tree.getSelectionModel().getSelectedNode();
var selectedParentNode = selectedNode.parentNode;
if (selectedParentNode == null) {
selectedNode.appendChild(newNode);
} else {
selectedParentNode.insertBefore(newNode, selectedNode);
}
setTimeout(function() {
treeEditor.editNode = newNode;
treeEditor.startEdit(newNode.ui.textNode);
}, 10);
}
function deleteHandler() {
tree.getSelectionModel().getSelectedNode().remove();
}
function modifyHandler() {
var selectedNode = tree.getSelectionModel().getSelectedNode();// 得到选中的节点
treeEditor.editNode = selectedNode;
treeEditor.startEdit(selectedNode.ui.textNode);
}
function viewHandler() {
var viewPanel = Ext.getCmp('tree-window-view');
var selectedNode = tree.getSelectionModel().getSelectedNode();
// 得到选中的节点
var tmpid = selectedNode.attributes.id;
var tmpname = selectedNode.attributes.text;
var tmpdes = selectedNode.attributes.description;
var tmphref = selectedNode.attributes.href;
win.setTitle(tmpname + '的介绍');
win.show();
var dataObj = {
id : tmpid,
name : tmpname,
des : tmpdes,
href : tmphref
}
var tmpTpl = new Ext.Template([
'<div style="margin:10px"><div style="margin:10px">编号:{id}</div>',
'<div style="margin:10px">名称:{name}</div>',
'<div style="margin:10px">描述:{des}</div>',
'<div style="margin:10px">链接:{href}</div></div></div>']);
tmpTpl.overwrite(viewPanel.body, dataObj);
}
});
二、拖拽中遇到的几个问题
要实现树的拖拽,只要将treePanel中的几个属性打开即可,在此不再赘述。本文着重整理了一些在实际操作中会遇到的几个问题:
1. 树间拖拽会导致原始树节点减少问题。
当一个节目从树A拖到树B上,A上的该节目默认会减少,有时候我们并不需要这种效果,要求树A保持不变,可采用如下解决方案:
对接收节点的树B添加一个事件,将拖拽过来的节点copy一下,即可避免树A节点减少。
tree.on('beforenodedrop', function(e){
var n = e.dropNode; // the node that was dropped
var copy = new Ext.tree.TreeNode( // copy it
Ext.apply({}, n.attributes)
);
e.dropNode = copy; // assign the copy as the new dropNode
});
上述方法不能处理子节点,下面的操作则可解决子节点问题
tree.on('beforenodedrop', function(e){
e.dropNode = copyDropNode(e.dropNode);
});
function copyDropNode(node){
var newNode = new Ext.tree.TreeNode(Ext.apply({}, node.attributes));
for(var i=0; i < node.childNodes.length; i++){
n = node.childNodes[i];
if(n){
newNode.appendChild(copyDropNode(n));
}
}
return newNode;
}
2. 飞回动画问题。
tree与panel之间的drag&drop,如下代码可正确实现tree与gridPanel之间的拖拽。
但有一点,tree节点被拖拽过去后会一个节点飞回树的动画,这不是我们所希望。
解决方案是在notifyDrop完成相应处理后要有一个为true的返回值。
MainGrid = new Ext.grid.GridPanel({
enableDragDrop: true,
ddGroup : 'TreeDD',
.....
});
var ddropTarget = new Ext.dd.DropTarget(MainGrid.getEl(), {
ddGroup: "TreeDD",
copy:false,
notifyDrop : function(dd, e, data){
var target = Ext.lib.Event.getTarget(e);
var rindex = MainGrid.getView().findRowIndex(target); // index of row where item is dropped
var destination = "";
// drag & drop from tree to grid
if(data.node != null)
{
if (rindex === false)
// ### drop into grid
destination = "GRID";
else
destination = MainGridStore.getAt(rindex).data.name;
data.node.remove();
//alert("Source: " + data.node.attributes.text + " Dest: " + destination);
}
// grid to grid
else
{
if (rindex === false) return false;
if (rindex == data.rowIndex) return false;
var rows = MainGrid.getSelectionModel().getSelections();
var nbRows = rows.length;
var cindex = dd.getDragData(e).rowIndex;
for(i = 0; i < nbRows; i++)
{
rowData = MainGridStore.getById(rows[i].id);
MainGridStore.remove(MainGridStore.getById(rows[i].id));
}
//MainGrid.getView().refresh();
// ### display source and destination
var sel="";
for(i = 0; i < data.selections.length; i++)
{
sel+= data.selections[i].data.name + " ";
}
//alert("Source: " + sel + " Dest: " + store.getAt(rindex).data.name);
}
}
});
For the TreePanel:
==============
MainTree = new Ext.tree.TreePanel({
title:'Folders',
split:true,
ddGroup : 'TreeDD',
enableDD:true,
dropConfig: {appendOnly:true},
...
});
function TreeBeforeNodeDrop(dropObj)
{
// from grid to tree
if(dropObj.data.grid != null)
{
// get all selected grid rows
var rows = MainGrid.getSelectionModel().getSelections();
var nbRows = rows.length;
for(i = 0; i < nbRows; i++)
{
MainGridStore.remove(MainGridStore.getById(rows[i].id));
}
//alert("source: " + dropObj.source.dragData.selections[0].data.name + " dest: " + //dropObj.target.attributes.text);
}
//from tree to tree
else
{
//alert("source: " + dropObj.dropNode.attributes.text + " dest: " + //dropObj.target.attributes.text);
}
}
转自:http://hi.baidu.com/lvjunnan/blog/item/c700891232a7aa0b203f2e50.html