100行代码解析Dojo树控件拖拽案例
案例设定:
创建2个树控件,左右排列。
使用拖动的方式,将树节点从左侧树控件拖拽的右侧树控件。
拖拽过程中右侧树控件要进行验证,确认是否可以方式拖拽中的节点。
放置的处理,识别要放置的节点,获取其信息并动态创建新的节点(基于基础类型进行实例化的过程)。
右侧树控件内(实例化之后的节点),同类型节点间支持拖动排序。
Dojo版本 1.10.3
图例1:创建2个树控件,左右排列。
图例2、3:
使用拖动的方式,将树节点从左侧树控件拖拽的右侧树控件。
拖拽过程中右侧树控件要进行验证,确认是否可以方式拖拽中的节点。
图例4
放置的处理,识别要放置的节点,获取其信息并动态创建新的节点(基于基础类型进行实例化的过程)。
图例5:拖拽之后的效果
图例6、7
右侧树控件内(实例化之后的节点),同类型节点间支持拖动排序。
案例代码:
<!DOCTYPEhtml><html><head><metahttp-equiv="Content-Type"content="text/html;charset=utf-8"><linkrel="stylesheet"type="text/css"href="../dojo_1.10.3/dijit/themes/claro/claro.css"/><scripttype="text/javascript"src="../dojo_1.10.3/dojo/dojo.js"></script><script>functionmakeTree(treedata,rootid,treenodeid,dndSkip,registry,ItemFileWriteStore,TreeStoreModel,dndSource,Tree){varstore2=newItemFileWriteStore({data:treedata});vartreeModel=newTreeStoreModel({store:store2,query:{"id":rootid},childrenAttrs:["children"]});vartree=newTree({model:treeModel,openOnClick:true,showRoot:true,autoExpand:true,persist:false,style:"width:400px;height:500px;"},treenodeid);tree.startup();varfoo=newdndSource(tree,{betweenThreshold:5,singular:true});foo.checkItemAcceptance=function(target,source,position){if(dndSkip)returnfalse;varflag=false;//判断是否可以拖拽vartar_type=registry.byNode(target.parentNode).item.type[0];varsrc_type=source.tree.selectedItem.type[0];flag=(tar_type===src_type+'_sp'&&position==="over")||(tar_type===src_type&&position!=="over");returnflag;};//dijit/tree/dndSource.js源码中明确提及要求开发者覆盖此方法。foo.itemCreator=function(nodes){varrst=newArray();for(vari=0;i<nodes.length;i++){varnode=nodes[i];varid=''+Math.floor(Math.random()*1000);varitem=registry.byNode(node).item;varrst2={id:id,name:item.name+"("+id+")",type:item.type};rst.push(rst2);}returnrst;};}require(["dijit/registry","dojo/data/ItemFileWriteStore","dijit/tree/TreeStoreModel","dijit/tree/dndSource","dijit/Tree","dojo/domReady!"],function(registry,ItemFileWriteStore,TreeStoreModel,dndSource,Tree){if(true){varrootid='root2';vartreedata={"identifier":"id",label:"name","items":[{id:rootid,name:'零件库',type:'static',children:[{id:'fdj_comp',name:'发动机',type:'static',children:[{id:'fdj1',name:'A01-发动机',parent:'fdj_comp',type:'fdj_comp'},{id:'fdj2',name:'A02-发动机',parent:'fdj_comp',type:'fdj_comp'},{id:'fdj3',name:'A03-发动机',parent:'fdj_comp',type:'fdj_comp'}]},{id:'jss_comp',name:'驾驶室',type:'static',children:[{id:'cm_comp',name:'车门',type:'static',children:[{id:'cm1',name:'左侧车门',parent:'cm_comp',type:'cm_comp'},{id:'cm2',name:'右侧车门',parent:'cm_comp',type:'cm_comp'}]},{id:'ys_com',name:'内饰颜色',type:'static',children:[{id:'heis',name:'黑色',parent:'ys_com',type:'ys_com'},{id:'mis',name:'米色',parent:'ys_com',type:'ys_com'},{id:'hongs',name:'红色',parent:'ys_com',type:'ys_com'}]}]}]}]};makeTree(treedata,rootid,'funtree1',true,registry,ItemFileWriteStore,TreeStoreModel,dndSource,Tree);}if(true){varrootid='root3';vartreedata={"identifier":"id",label:"name","items":[{id:rootid,name:'配置选择',type:'static',children:[{id:'zfdj',name:'发动机',type:'fdj_comp_sp',children:[]},{id:'zcm',name:'车门',type:'cm_comp_sp',children:[]},{id:'zys',name:'内饰颜色',type:'ys_com_sp',children:[]}]}]};makeTree(treedata,rootid,'funtree2',false,registry,ItemFileWriteStore,TreeStoreModel,dndSource,Tree);}});</script></head><bodyclass="claro"><tableborder="1"><tr><td><divid="funtree1"/></td><td><divid="funtree2"/></td></tr></table></body></html>
重点分析:
1.使用的dojo组件:
"dijit/registry"//用于获取组件,"dojo/_base/array"//用于整理数据,"dojo/data/ItemFileWriteStore"//用于包装数据,"dijit/tree/TreeStoreModel"//用于转换数据(Tre要求),"dijit/tree/dndSource"//用于处理拖拽,"dijit/Tree"//树控件
2. 构建符合树控件模型接受的数据结构:
varrootid='root3';vartreedata={"identifier":"id",label:"name","items":[{id:rootid,name:'配置选择',type:'static',children:[{id:'zfdj',name:'发动机',type:'fdj_comp_sp',children:[]},{id:'zcm',name:'车门',type:'cm_comp_sp',children:[]},{id:'zys',name:'内饰颜色',type:'ys_com_sp',children:[]}]}]};
3. 拖动对象的处理:
3.1 注册:
varfoo=newdndSource(tree,//指向目标树控件{betweenThreshold:5,//支持拖动至目标节点的“前位置”和“后位置”singular:true});//制定拖动的是1个目标节点(不含其子节点)
3.2 判定拖动的有效性:
foo.checkItemAcceptance=function(target,source,position){//覆盖函数checkItemAcceptance,返回true允许放置,返回false不允许放置if(dndSkip)returnfalse;//案例使用,左侧树始终不允许放置varflag=false;//判断是否可以拖拽vartar_type=registry.byNode(target.parentNode).item.type[0];//案例使用,右侧树允许同类型节点放置varsrc_type=source.tree.selectedItem.type[0];flag=(tar_type===src_type+'_sp'&&position==="over")||(tar_type===src_type&&position!=="over");returnflag;};
3.3 创建目标树节点:
//dijit/tree/dndSource.js源码中明确提及要求开发者覆盖此方法。foo.itemCreator=function(nodes){varrst=newArray();for(vari=0;i<nodes.length;i++){varnode=nodes[i];varid=''+Math.floor(Math.random()*1000);varitem=registry.byNode(node).item;varrst2={id:id,name:item.name+"("+id+")",type:item.type};rst.push(rst2);}returnrst;};
这部分很重要却很难了解到,在dijit/tree/dndSource.js的源码中明确提及需要开发者进行覆盖。而官方文档的介绍内容中并没有提及。
备注:
关于Dojo官方给出的树控件的介绍案例(https://dojotoolkit.org/reference-guide/1.10/dijit/Tree.html#dijit-tree),其使用的是"dojo/store/Memory",其数据结构
varmyStore=newMemory({data:[{id:'world',name:'Theearth',type:'planet',population:'6billion'},{id:'AF',name:'Africa',type:'continent',population:'900million',area:'30,221,532sqkm',timezone:'-1UTCto+4UTC',parent:'world'},……{id:'SA',name:'SouthAmerica',type:'continent',parent:'world'}],getChildren:function(object){returnthis.query({parent:object.id});}});
换言之其使用的是一维数组的数据构,加之数据对象中的parent属性指向父节点,完成树模型的构建。但是这个方法在功能上有缺陷:
一次性构建树控件,可以。
树控件中节点的拖动,可以。
树控件中节点的排序,不可以,因为其基础模型是一维数组,无法实现或者难以实现针对某一树节点的子节点序列进行排序的处理。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。