2015-11-03 4 views
5

Ich habe die folgende Situation, wo ich brauche, damit ein Benutzer Objekte aus einer Liste und zieht auszuwählen/sie in einen bestimmten Schlitz fallen:Kombination AngularJS, jQueryUI, Angular-Drag-Drop für sortierte Liste

enter image description here

Die Objekte können ein- bis dreimal so groß sein wie ein Steckplatz. Wenn also ein Benutzer Objekt 1 zu Slot 0 zieht, belegt er nur Slot 0 (startSlot = 0 und endSlot = 0). Wenn ein Benutzer jedoch Objekt 3 zu Slot 3 zieht, belegt er die Slots 3, 4 und 5 (startSlot = 3 und endSlot = 5).

Sobald die Objekte in die Slots fallen gelassen wurden, kann ein Benutzer die Objekte neu anordnen, indem er auf die Objekte in den Slots klickt und diese nach oben und unten zieht. Objekte können sie nicht überlappen:

enter image description here

I Angular verwenden, so dass ich eine Liste von Objekten aus einer Datenbank zu lesen und ich habe eine Variable für die Anzahl der Slots. Ich habe ein paar Lösungen versucht. Ich glaube, dass die Verwendung von jQuery und jQueryUI ziehbar, abwerfbaren und sortierbar ist Teil der Lösung, hier ist die erste Geige demonstriert Drag/Drop und sortierbar:

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

Das Problem mit dieser Geige ist, dass ich einen Satz benötigen Anzahl der Steckplätze. Sobald ein Objekt in den Slots platziert wird, ersetzt es 1 bis 3 Slots, abhängig von der Größe des Objekts. Die zweite Geige unten integriert AngularJS:

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

Das hier Problem ist, dass ich weiß, dass ich irgendeine Art von Gitter müssen die Objekte schnappen, um einmal aus der Objektliste gezogen. Das Ergebnis, das ich suche ist eine json Liste der Objekte in den ihnen zugewiesenen Slots:

[{id: obj1, startSlot: 0, endSlot: 0}, {id: obj3, startSlot: 3, endSlot

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

Aber ich habe Probleme versuchen, dass Integration in meine Geige zu bekommen zu testen: Angular Drag-Drop befindet codf0rmer der hier müssten 5}]

ich bin die Lösung auch sicher . Dies ist eine interessante Herausforderung, auf die ich mich schon eine Weile konzentriert habe. Wenn jemand Ihnen behilflich sein könnte, würde ich das sehr schätzen. Vielen Dank für Ihre Zeit.

+0

Sehen Sie sich https://github.com/angular-ui/ui-sortable an, das ist Teil der beliebten AngularUI-Komponentensuite. Sie unterstützen verbundene Listen, aber Ihre Artikelgrößenanforderung würde wahrscheinlich eine gewisse Kundenbehandlung der Listenelemente erfordern, sollte aber bei der Verwendung der Ereignisse einfach sein. Ich habe es bereits erfolgreich in einem Projekt verwendet. Leider habe ich momentan keine Zeit, um auf Ihre spezifischen Anforderungen einzugehen. – Beyers

+0

Sie können auch ein benutzerdefiniertes Modul mithilfe der HTML 5-Drag & Drop-API erstellen. Es ist ziemlich einfach zu implementieren. – malinkody

Antwort

5

Ich begann eine grundlegende Implementierung Ihrer Anforderungen mit der HTML5 Drag & Drop API und jQuery. Die API ist leicht und erfordert keine Skripte von Drittanbietern. Der Code sollte einfach anzupassen sein. Das mitgelieferte Beispiel ist nur ein Ausgangspunkt und auf keinen Fall produktionsbereit und sollte vor der Verwendung optimiert und möglicherweise in ein jQuery-Modul umgewandelt werden. Dies würde die Wiederverwendbarkeit des Moduls erhöhen.

Hinterlassen Sie weitere Fragen über den Code in den Kommentaren.

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 ohne sortierbar:

(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; 
} 

Bearbeiten: Das folgende Skript hat auch Sortierung hinzugefügt. Ich habe dieses Beispiel nicht stressgetestet und es kann unter bestimmten Bedingungen nicht funktionieren.

(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)); 
+0

das ist großartig, genau das, was ich brauchte, um über den 'Buckel' hinwegzukommen und eine Lösung für unsere Kunden zu bieten. Die Geige ist perfekt, danke nochmal für deine Hilfe. –

Verwandte Themen