结合 AngularJS、jQueryUI、Angular-Drag-Drop 进行排序列表

Posted

技术标签:

【中文标题】结合 AngularJS、jQueryUI、Angular-Drag-Drop 进行排序列表【英文标题】:Combining AngularJS, jQueryUI, Angular-Drag-Drop for sorted list 【发布时间】:2016-02-03 22:10:14 【问题描述】:

我有以下情况,我需要允许用户从列表中选择对象并将它们拖放到某个插槽中:

对象可以是插槽大小的一到三倍。所以如果用户将Object 1拖到Slot 0,那么它只占用Slot 0(startSlot = 0 and endSlot = 0)。但是,如果用户将对象 3 拖到 Slot 3,则它会占用 Slot 3、4 和 5(startSlot = 3 和 endSlot = 5)。

将对象放入插槽后,用户可以通过单击并在插槽中上下拖动对象来重新排序对象。对象不能相互重叠:

我正在使用 Angular,因此我正在从数据库中读取对象列表,并且我有一个用于表示插槽数的变量。我尝试了几种解决方案。我相信使用 jQuery 和 jQueryUI 可拖动、可放置和可排序是解决方案的一部分,这里是第一个演示拖放和可排序的小提琴:

http://jsfiddle.net/mduvall216/6hfuzvws/4/

这个小提琴的问题是我需要一定数量的插槽。将对象放入插槽后,它会根据对象的大小替换 1 到 3 个插槽。下面的第二个小提琴集成了AngularJS:

http://jsfiddle.net/mduvall216/zg5x4b6k/4/

这里的问题是我知道我需要某种类型的网格来捕捉对象一旦从对象列表中拖动。我正在寻找的结果是其分配的插槽中的对象的 json 列表:

[id:obj1,startSlot:0,endSlot:0,id:obj3,startSlot:3,endSlot:5]

我也确定解决方案需要位于此处的 codf0rmer 的 Angular Drag-Drop:

https://github.com/codef0rmer/angular-dragdrop

但是我在尝试将其集成到我的小提琴中进行测试时遇到了问题。这是我一直在旋转的一个有趣的挑战,如果有人可以提供帮助,将不胜感激。感谢您的宝贵时间。

【问题讨论】:

看看 github.com/angular-ui/ui-sortable ,它是流行的 AngularUI 组件套件的一部分。它们支持连接列表,但您的项目大小要求可能需要一些客户处理列表项目,但在使用事件时应该直截了当。我之前在一个项目中成功使用过它。很遗憾,我现在没有时间研究您的具体要求。 您还可以使用 html 5 拖放 API 创建自定义模块。它很容易实现。 【参考方案1】:

我使用 HTML5 拖放 API 和 jQuery 开始了您的要求的基本实现。 API 是轻量级的,不需要任何 3rd 方脚本。代码应该易于定制。提供的示例只是一个起点,绝不是生产就绪,应该在使用前进行优化并可能变成一个 jQuery 插件模块。这将增加模块的可重用性。

在 cmets 中留下有关代码的任何其他问题。

JSFiddle Example without sortable:

JSFiddle with sortable

html:

<ul class="select-list">
    <li class="header">Object List</li>
    <li data-slots="1" class="s1">Object 1</li>
    <li data-slots="2" class="s2">Object 2</li>
    <li data-slots="3" class="s3">Object 3</li>
</ul>
<ul class="drop-list" id="sortable">
    <li>Slot 1</li>
    <li>Slot 2</li>
    <li>Slot 3</li>
    <li>Slot 4</li>
    <li>Slot 5</li>
    <li>Slot 6</li>
    <li>Slot 7</li>
    <li>Slot 8</li>
    <li>Slot 9</li>
    <li>Slot 10</li>
    <li>Slot 11</li>
    <li>Slot 12</li>
    <li>Slot 13</li>
</ul>

没有可排序的javascript

(function ($, undefined) 
    // document ready function
    $(function () 
        init();

        $('ul.select-list').on(
            'dragstart': dragstart,
                'dragend': dragend
        , 'li');

        $('ul.drop-list').on(
            'dragenter dragover': dragover,
                'dragleave': dragleave,
                'drop': drop
        , 'li.dropzone');
    );

    // Initializes the lists
    function init() 
        $('ul.select-list').find('li').not('[class="header"]').each(function () 
            var height = getSlotHeight() * $(this).data('slots');
            $(this).height(height);
        ).attr('draggable', true);

        $('ul.drop-list').find('li').each(function () 
            $(this).height(getSlotHeight());
        ).addClass('dropzone');
    

    // Get the height of grid slots
    function getSlotHeight() 
        return 20;
    

    /**
    * Checks whether target is a kompatible dropzone
    * A dropzone needs the dropzone class
    * and needs to have enough consequent slots to drop the object into
    */
    function isDropZone(target, draggedObj) 
        var isDropZone = true;
        var slots = draggedObj.data('slots');
        for (var i = 1; i < slots; i++) 
            target = target.next();
            if (target.size() == 0 || !target.hasClass('dropzone')) 
                isDropZone = false;
                break;
            
        
        return isDropZone;
        

    /* 
    * The following events are executed in the order the handlers are declared
    * dragstart being first and dragend being last
    */

    // dragstart event handler
    function dragstart(e) 
        e.stopPropagation();
        var dt = e.originalEvent.dataTransfer;
        dt.effectAllowed = 'move';
        dt.setData('text/html', '');
        $('ul.select-list').data(
            draggedObj: $(this)
        );
    

    // dragover event handler
    function dragover(e) 
        e.preventDefault();
        e.stopPropagation();
        var data = $('ul.select-list').data();
        if (!data.draggedObj || !isDropZone($(this), data.draggedObj)) 
            e.originalEvent.dataTransfer.dropEffect = 'none';
            return;
        
        e.originalEvent.dataTransfer.dropEffect = 'move';
        var item = $(this).addClass('dragover');
        var slots = data.draggedObj.data('slots');
        for (var i = 1; i < slots; i++) 
            item = item.next('li').addClass('dragover');
        
        return false;
    

    // dragleave event handler
    function dragleave(e) 
        e.preventDefault();
        e.stopPropagation();
        var data = $('ul.select-list').data();
        if (!data.draggedObj || !isDropZone($(this), data.draggedObj)) 
            return;
        
        var item = $(this).removeClass('dragover');
        var slots = data.draggedObj.data('slots');
        for (var i = 1; i < slots; i++) 
            item = item.next('li').removeClass('dragover');
        
        return false;
    

    // drop event handler
    function drop(e) 
        e.stopPropagation();
        e.preventDefault();
        var data = $('ul.select-list').data();
        if (data.draggedObj || !isDropZone($(this), data.draggedObj)) 
            data.target = $(this);
            data.draggedObj.trigger('dragend');
        
        return false;
    

    // dragend event handler
    function dragend(e) 
        var data = $('ul.select-list').data();
        if (data.draggedObj && data.target && isDropZone(data.target, data.draggedObj)) 
            var item = data.target.hide();
            var slots = data.draggedObj.data('slots');
            for (var i = 1; i < slots; i++) 
                item = item.next('li').hide();
            
            data.target.before(data.draggedObj);
        

        data.target = undefined;
        data.draggedObj = undefined;
        $('ul.drop-list').find('li').removeClass('dragover');
    
(jQuery));

css:

ul 
     list-style-type: none;
     margin: 0;
     padding: 0;
     float: left;
 
 li 
     width: 150px;
 
 li.header 
     height:20px;
     font-weight:bold;
 
 .select-list li 
     margin-bottom: 10px;
 
 .drop-list 
     margin-left:20px;
 
 .drop-list li 
     background-color: #ccc;
     border-left: 1px solid black;
     border-right: 1px solid black;
     border-top: 1px solid black;
 
 .drop-list li.dragover 
     background-color:#fff;
 
 .drop-list li:last-child 
     border-bottom: 1px solid black;
 
 li.s1 
     background-color: #FFE5E5;
 
 li.s2 
     background-color: #C6D4FF;
 
 li.s3 
     background-color: #C6FFE3;
 

编辑:以下脚本还添加了排序。我没有对这个示例进行压力测试,它在某些条件下可能无法执行。

(function ($, undefined) 
    // document ready function
    $(function () 
        init();

        $('ul.select-list,ul.drop-list').on(
            'dragstart': dragstart,
                'dragend': dragend
        , 'li.object').on('dragenter dragover', listDragover);

        $('ul.drop-list').on(
            'dragenter dragover': dragover,
                'dragleave': dragleave,
                'drop': drop
        , 'li.dropzone');
    );

    // Initializes the lists
    function init() 
        $('ul.select-list').find('li').not('[class="header"]').each(function () 
            var height = getSlotHeight() * $(this).data('slots');
            $(this).height(height);
        ).attr('draggable', true).addClass('object');

        $('ul.drop-list').find('li').each(function () 
            $(this).height(getSlotHeight());
        ).addClass('dropzone');
    

    // Get the height of the grid
    function getSlotHeight() 
        return 20;
    

    /**
     * Checks whether target is a kompatible dropzone
     * A dropzone needs the dropzone class
     * and needs to have enough consequent slots to drop the object into
     */
    function isDropZone(target, draggedObj) 
        var isDropZone = true;
        var slots = draggedObj.data('slots');
        for (var i = 1; i < slots; i++) 
            target = target.next('li');
            if (target.size() == 0 || !target.hasClass('dropzone')) 
                if (!target.is(draggedObj)) 
                    isDropZone = false;
                    break;
                 else 
                    i--;
                
            
        
        return isDropZone;
    

    // dragstart event handler
    function dragstart(e) 
        e.stopPropagation();
        var dt = e.originalEvent.dataTransfer;
        dt.effectAllowed = 'move';
        dt.setData('text/html', ''); 
        $('ul.select-list').data(
            draggedObj: $(this)
        );        
    

    // dragover list event handler
    function listDragover(e) 
        e.preventDefault();
        e.stopPropagation();
        e.originalEvent.dataTransfer.dropEffect = 'none';
        var data = $('ul.select-list').data();
        if (data.draggedObj) 
            var item = data.draggedObj;
            item.hide();
            if (data.draggedObj.closest('ul').is('ul.drop-list')) 
                var slots = item.data('slots');
                for (var i = 0; i < slots; i++) 
                    item = item.next('li').show();
                
            
        
        return false;
    

    // dragover event handler
    function dragover(e) 
        e.preventDefault();
        e.stopPropagation();
        var data = $('ul.select-list').data();
        if (!data.draggedObj || !isDropZone($(this), data.draggedObj)) 
            e.originalEvent.dataTransfer.dropEffect = 'none';
            return;
        
        e.originalEvent.dataTransfer.dropEffect = 'move';
        var item = $(this).addClass('dragover');
        var slots = data.draggedObj.data('slots');
        for (var i = 1; i < slots; i++) 
            item = item.next('li');
            if (!item.is(data.draggedObj)) 
                item.addClass('dragover');
             else 
                i--;
            
        
        return false;
    

    // dragleave event handler
    function dragleave(e) 
        e.preventDefault();
        e.stopPropagation();
        var data = $('ul.select-list').data();
        if (!data.draggedObj || !isDropZone($(this), data.draggedObj)) 
            return;
        
        var item = $(this).removeClass('dragover');
        var slots = data.draggedObj.data('slots');
        for (var i = 1; i < slots; i++) 
            item = item.next('li');
            if (!item.is(data.draggedObj)) 
                item.removeClass('dragover');
             else 
                i--;
            
        
        return false;
    

    // drop event handler
    function drop(e) 
        e.stopPropagation();
        e.preventDefault();
        var data = $('ul.select-list').data();
        if (data.draggedObj || !isDropZone($(this), data.draggedObj)) 
            data.target = $(this);
            data.draggedObj.trigger('dragend');
        
        return false;
    

    // dragend event handler
    function dragend(e) 
        var data = $('ul.select-list').data();
        var target = data.target;
        if (data.draggedObj && !target && data.draggedObj.closest('ul').is('ul.drop-list')) 
            target = data.draggedObj.next('li');
        
        if (data.draggedObj && target && isDropZone(target, data.draggedObj)) 
            data.draggedObj = data.draggedObj.insertBefore(target);
            var item = target.hide();
            var slots = data.draggedObj.data('slots');
            for (var i = 1; i < slots; i++) 
                item = item.next('li').hide();
            
        
        if (data.draggedObj) 
            data.draggedObj.show();
        
        data.target = undefined;
        data.draggedObj = undefined;
        $('ul.drop-list').find('li').removeClass('dragover');
    
(jQuery));

【讨论】:

这很棒,正是我需要克服“困难”并为我们的客户提供解决方案。小提琴很完美,再次感谢您的帮助。

以上是关于结合 AngularJS、jQueryUI、Angular-Drag-Drop 进行排序列表的主要内容,如果未能解决你的问题,请参考以下文章

结构-行为-样式 - Angularjs 环境下Ztree结合JqueryUI实现拖拽

诚聘web前端开发

使 ngDialog 可拖动 AngularJs jQueryUI

AngularJS - JQuery UI 是不是适用于内置的 JQLite [重复]

一篇入门AngularJS

AngularJS 四 服务