Drag object into sortable list - AngularJS

There is no angular-magic that can help you find the position of a new or moved element, but it's easy to do with jQuery. I've created an example of the jQueryUI-demo wrapping sortable and draggable in directives:

http://plnkr.co/edit/aSOlqR0UwBOXgpQSFKOH?p=preview

<ul>
  <li my-draggable="#sortable" class="ui-state-highlight">Drag me down</li>
</ul>

<ul my-sortable id="sortable">
  <li class="ui-state-default" ng-repeat="item in items">{{item.name}}</li>
</ul>

Value of my my-draggable is the id to the corresponding my-sortable-element. my-draggable is otherwise pretty straight forward:

app.directive('myDraggable',function(){

  return {
    link:function(scope,el,attrs){
      el.draggable({
        connectToSortable: attrs.myDraggable,
        helper: "clone",
        revert: "invalid"
    });
    el.disableSelection();
    }
  }
})

In my-sortable I listen to the deactivate event which indicates that an element has been dropped. from is the position of the element in the array that is the source of ng-repeat. ng-repeat creates a child scope for each element with an $index variable indicating the position of the current element in the array. If $index is undefined I know that it's a new element (might be a better way to determine this, but it works for this example). to is the new position of the item. I $emit a 'my-sorted' event if an existing element was moved or a 'my-created' event if a new item was added.

app.directive('mySortable',function(){
  return {
    link:function(scope,el,attrs){
      el.sortable({
        revert: true
      });
      el.disableSelection();

      el.on( "sortdeactivate", function( event, ui ) { 
        var from = angular.element(ui.item).scope().$index;
        var to = el.children().index(ui.item);
        if(to>=0){
          scope.$apply(function(){
            if(from>=0){
              scope.$emit('my-sorted', {from:from,to:to});
            }else{
              scope.$emit('my-created', {to:to, name:ui.item.text()});
              ui.item.remove();
            }
          })
        }
      } );
    }
  }
})

In the controller I create the items-array and listen to the events:

$scope.items = [
  {name:'Item 1'},
  {name:'Item 2'},
  {name:'Item 3'},
  {name:'Item 4'},
];

$scope.$on('my-sorted',function(ev,val){
  // rearrange $scope.items
  $scope.items.splice(val.to, 0, $scope.items.splice(val.from, 1)[0]);
})

$scope.$on('my-created',function(ev,val){
  // create new item at position 
  $scope.items.splice(val.to, 0,
    {name:'#'+($scope.items.length+1)+': '+val.name});
})

As you can see, when you add or move an element the model in the scope gets updated.

These directives are not very general - you might have to do some adjustment to get them to work with your application.


You should be able to do every thing you need in your link function.

myapp.directive("menuDrag", function () {

    return{
        restrict: "A",
        link:     function (scope, element, attrs) {
            var item = $(".draggable").draggable(
                {
                    snap:   true,
                    revert: false,
                    // scope:".dropable"
                    //scope: "tasks"
                }
            )
            var target = $(".dropable").droppable({

                greedy:     true,
                hoverClass: "warning",
                accept:     ".draggable"
            })


            item.on("drag", function (evt) {
                item.css = ('background-color', 'red');
                //evt.stopPropagation();
                //evt.preventDefault();
            })


            target.on("over", function (evt) {

                target.css('background-color', 'blue')

                return false;
            });
            target.on("out", function (evt) {
                dropBox.css('background_color', 'red');


                return false;
            });
            target.on("drop", function (evt) {
                alert("Droped");
                return false;
            });
            //dragEnterLeave(evt);
        }
    }
})