100行代码解析Dojo树控件拖拽案例

案例设定:

  1. 创建2个树控件,左右排列。
  2. 使用拖动的方式,将树节点从左侧树控件拖拽的右侧树控件。
  3. 拖拽过程中右侧树控件要进行验证,确认是否可以方式拖拽中的节点。
  4. 放置的处理,识别要放置的节点,获取其信息并动态创建新的节点(基于基础类型进行实例化的过程)。
  5. 右侧树控件内(实例化之后的节点),同类型节点间支持拖动排序。

Dojo版本 1.10.3

图例1:创建2个树控件,左右排列。

图例2、3:

使用拖动的方式,将树节点从左侧树控件拖拽的右侧树控件。

拖拽过程中右侧树控件要进行验证,确认是否可以方式拖拽中的节点。

图例4

放置的处理,识别要放置的节点,获取其信息并动态创建新的节点(基于基础类型进行实例化的过程)。

图例5:拖拽之后的效果

图例6、7

右侧树控件内(实例化之后的节点),同类型节点间支持拖动排序。

案例代码:

<!DOCTYPE html>
<html > <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <link rel="stylesheet" type="text/css" href="../dojo_1.10.3/dijit/themes/claro/claro.css"/>
        <script type="text/javascript"  src="../dojo_1.10.3/dojo/dojo.js" ></script> 
        <script>
            function makeTree(treedata, rootid, treenodeid, dndSkip
                    , registry, ItemFileWriteStore, TreeStoreModel, dndSource, Tree) {
                var store2 = new ItemFileWriteStore({data: treedata});
                var treeModel = new TreeStoreModel({
                    store: store2
                    , query: {"id": rootid}
                    , childrenAttrs: ["children"]
                });
                
                var tree = new Tree({
                    model: treeModel, openOnClick: true, showRoot: true, autoExpand: true,
                    persist: false, style: "width:400px;height:500px;"
                }, treenodeid);
                tree.startup();

                var foo = new dndSource(tree, {betweenThreshold: 5, singular: true});
                foo.checkItemAcceptance = function (target, source, position) {
                    if (dndSkip)
                        return false;
                    var flag = false;//判断是否可以拖拽
                    var tar_type = registry.byNode(target.parentNode).item.type[0];
                    var src_type = source.tree.selectedItem.type[0];
                    flag = (tar_type === src_type + '_sp' && position === "over") || (tar_type === src_type && position !== "over");
                    return flag;
                };
                // dijit/tree/dndSource.js  源码中明确提及要求开发者覆盖此方法。
                foo.itemCreator = function (nodes) {
                    var rst = new Array();
                    for (var i = 0; i < nodes.length; i++) {
                        var node = nodes[i];
                        var id = '' + Math.floor(Math.random() * 1000);
                        var item = registry.byNode(node).item;
                        var rst2 = {id: id, name: item.name + "(" + id + ")", type : item.type}; 
                        rst.push(rst2);
                    }
                    return rst;
                };
            }
            
            require(["dijit/registry", "dojo/data/ItemFileWriteStore", "dijit/tree/TreeStoreModel", "dijit/tree/dndSource", "dijit/Tree", "dojo/domReady!"],
                    function (registry, ItemFileWriteStore, TreeStoreModel, dndSource, Tree) {
                        if (true) {
                            var rootid = 'root2';
                            var treedata = {"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) {
                            var rootid = 'root3';
                            var treedata = {"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>
    <body class="claro">
        <table border="1">
            <tr>
                <td><div id="funtree1"/></td>
                <td><div id="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. 构建符合树控件模型接受的数据结构:

var rootid = 'root3';
var treedata = {"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 注册:

var foo = new dndSource(
    tree,                 //指向目标树控件
    {betweenThreshold: 5, //支持拖动至目标节点的“前位置”和“后位置”
    singular: true});    //制定拖动的是1个目标节点(不含其子节点)

3.2 判定拖动的有效性:

foo.checkItemAcceptance = function (target, source, position) {    //覆盖函数checkItemAcceptance ,返回true允许放置,返回false不允许放置
    if (dndSkip)
        return false;                                                      //案例使用,左侧树始终不允许放置
    var flag = false;//判断是否可以拖拽
    var tar_type = registry.byNode(target.parentNode).item.type[0];        //案例使用,右侧树允许同类型节点放置
    var src_type = source.tree.selectedItem.type[0];
    flag = (tar_type === src_type + '_sp' && position === "over") || (tar_type === src_type && position !== "over");
    return flag;
};

3.3 创建目标树节点:

// dijit/tree/dndSource.js  源码中明确提及要求开发者覆盖此方法。
foo.itemCreator = function (nodes) {
    var rst = new Array();
for (var i = 0; i < nodes.length; i++) {
		var node = nodes[i];
		var id = '' + Math.floor(Math.random() * 1000);
		var item = registry.byNode(node).item;
		var rst2 = {id: id, name: item.name + "(" + id + ")", type : item.type}; 
		rst.push(rst2);
	}
	return rst;
};

这部分很重要却很难了解到,在dijit/tree/dndSource.js的源码中明确提及需要开发者进行覆盖。而官方文档的介绍内容中并没有提及。

备注:

关于Dojo官方给出的树控件的介绍案例(https://dojotoolkit.org/reference-guide/1.10/dijit/Tree.html#dijit-tree),其使用的是"dojo/store/Memory",其数据结构

var myStore = new Memory({
        data: [
            { id: 'world', name:'The earth', type:'planet', population: '6 billion'},
            { id: 'AF', name:'Africa', type:'continent', population:'900 million', area: '30,221,532 sq km',
                    timezone: '-1 UTC to +4 UTC', parent: 'world'},         
               ……
                { id: 'SA', name:'South America', type:'continent', parent: 'world' }
        ],
        getChildren: function(object){
            return this.query({parent: object.id});
        }
    });

换言之其使用的是一维数组的数据构,加之数据对象中的parent属性指向父节点,完成树模型的构建。但是这个方法在功能上有缺陷:

  1. 一次性构建树控件,可以。
  2. 树控件中节点的拖动,可以。
  3. 树控件中节点的排序,不可以,因为其基础模型是一维数组,无法实现或者难以实现针对某一树节点的子节点序列进行排序的处理。

原文地址:http://blog.51cto.com/zglei/2107635

时间: 2024-08-04 17:51:18

100行代码解析Dojo树控件拖拽案例的相关文章

控件拖拽置换位置

简单的一个控件拖拽交换位置Demo,有些场景下会用到,关于此类功能网上有很多例子,而且Google官方API中也有相应的接口,对这种业务需求进行了一定逻辑封装.这里没有采用官方接口,单纯的从触摸事件入手来简单的实现控件位置交换. 写代码之前先理清楚实现的思路,这里从控件的触摸事件入手,触摸事件有ACTION_DOWN.ACTION_MOVE.ACTION_UP这几个状态,下面先把实现逻辑的思路写出来: 1.界面加载完成后用一个List记录各个子控件相对于父控件的坐标,同时将这些子控件放入一个Li

【WPF】总结窗口和控件拖拽的实现

前文 本文只对笔者学习掌握的一般的拖动问题的实现方法进行整理和讨论,包括窗口.控件等内容的拖动. 希望本文能对一些寻找此问题的解决方法的人和一些刚入门的人一些帮助.笔者为WPF初学者,能得到各位的批评指正也是荣幸万分.有更好更多的方法,劳烦与我分享,不胜感激. 本文的各种实现方法其他博客中也都有提及,很多文章内容详实,有图有代码,笔者就不重复造轮子了.就写写自己的一些理解吧. 关键词 Window, UserControls, drag, Thumb 参考资料 http://www.cnblog

C#中实现控件拖拽效果(How to DragDrop Control in C#)

当产品间需要交互实现数据传递,或产品需要从外部导入文件时,通过控件拖拽来实现是个不错的选择.在UI上支持控件拖拽,可极大提升用户体验.拖拽本身并不神秘,它的本质实际是一个数据交换的过程.控件接受从其他地方来的数据,并进行处理.数据交换有多种方法,Windows中剪贴板可能就是用的最多,但最不被注意的一种方法.下面介绍用C#实现控件拖拽,并通过剪切板交换数据. 对于拖拽的对象,需要在MouseDown或ItemDrag中调用DoDragDrop,传递要拖拽的数据对象并触发拖拽.总的来说,当用户调用

第一行代码Android-------第二章控件部分

一.控件 1.大小 match_parenr:与父布局大小一样 fill_parent:与match_parent一样 wrap_content:控件大小刚好包住里面内容 2.TextView   在界面上显示一段文字 android:text = "显示的文字" android:gravity:"文字对齐方式" //可选值有top.bottom.right.center,可以用|来指定多个值 android:textSize:文字大小 android:textCo

duilib中控件拖拽功能的实现方法(附源码)

转载请说明原出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/41144283 duilib库中原本没有显示的对控件增加拖拽的功能,而实际使用过程中拖拽功能也是有用武之地的.看群里有人问题duilib怎么支持拖拽,我也就写这篇文章说明一下duilib实现控件拖拽的方法. 当我刚接触duilib不就的时候,考虑过duilib拖拽这个功能,当时的想法是,在xml布局中设置一个浮动的控件,正常状态下他是隐藏的,当出发了拖拽条件后将他显示并且

Qt qml treeview 树控件

qml并没有提供树控件,只能自己写了.model仍然用ListModel对象,弄成层级的就行.delegate必须用loader动态的增加子控件,如此而已. [先看效果] [下载] http://download.csdn.net/detail/surfsky/8406181 [核心代码] 1 import QtQuick 2.1 2 import QtQuick.Controls 1.0 3 4 5 /** 6 树控件 7 作者:surfsky.cnblogs.com 2014-10 8 协议

MFC树控件的使用

HICON hIcon[4];      // 图标句柄数组 // 加载三个图标,并将它们的句柄保存到数组 hIcon[0] = theApp.LoadIcon(IDI_ICON_TREE_CLOSE); hIcon[1] = theApp.LoadIcon(IDI_ICON_TREE_EXPEND); hIcon[2] = theApp.LoadIcon(IDI_ICON_VIDEO_USER_OFFLINE); hIcon[3]=theApp.LoadIcon(IDI_ICON_VIDEO_

我的开源框架之树控件

需求: 1.根据无限级的树形结构的json生成树菜单 2.树样式可以是图标类型和简单类型 3.可以自定义节点的图标 4.支持复选框 5.支持懒加载方式请求数据 6.支持节点点击事件 7.只有右键菜单[未实现] 8.支持拖拽调整节点[未实现] 实现图例 客户代码 1 <body> 2 <div id="Container" style="padding:10px; margin:0 auto;width:800px;height:300px;padding-t

Ext树控件第一次勾选父节点子节点没选中

项目中同事提出了这样一个bug,为了解决这个问题,牛逼闪闪的码农登场了,该码农专制各种公交抢上抢下抢座位,专制各种偷鸡摸狗,专制各种广场舞大妈扰民,专制各种不服,总之哪里有不平哪里就有撒哥的身影 扯淡结束,进入今天的正题 问题: 第一次勾选父节点子节点竟然没选中,逆天了啊 初步分析: 可能是之前代码的逻辑错误造成的,随进入调试阶段... 调试中发现该参数为空(原来写代码的也太没素质了), 没有内容然后想到没有内容导致下面的子节点不能便利出来,所以出来后没有选中,嘿嘿 然后加上该参数,接着查看然后