<!DOCTYPE html>
<html ng-app="ui-sortable-multiselect" ng-controller="MultiSelectDemo">

  <head>
    <script data-require="jquery@*" data-semver="2.0.3" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
    <link data-require="jqueryui@*" data-semver="1.10.0" rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.10.0/css/smoothness/jquery-ui-1.10.0.custom.min.css" />
    <script data-require="jqueryui@*" data-semver="1.10.0" src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.10.0/jquery-ui.js"></script>
    <script data-require="angular.js@1.2.17" data-semver="1.2.17" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.17/angular.min.js"></script>
    <script data-require="underscore.js@*" data-semver="1.6.0" src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
    <script src="https://rawgit.com/angular-ui/ui-sortable/d9911be26fb0227659c53fe9f4a4147bb2874dc0/src/sortable.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>

  <body>
    <h2>Multi-select sortable with AngularJS</h2>
    <p>
      <code>Click</code> to select individual items</p>
    <p>
      <code>Ctrl + Click</code> to select multiple items</p>
    <p>
      <code>Shift + Click</code> to select multiple items</p>
    <div class="list-manager">
      <div class="loading-overlay" ng-show="actingOnThings">
        <span class="fa fa-spin fa-spinner"></span> Doing a thing...
      </div>
      <div>
        <input type="text" />
        <button>Add</button>
      </div>
      <div class="list">
        <h2>Trash, And Similar Things</h2>
        <ul multi-select="itemsInTrash" multi-select-function="selectItem" ui-sortable="sortableOptions" class="list list-container" ng-model="itemsInTrash"></ul>
        <!--
          <button class="selectAll" data-bind="click: selectAllItemsIn.bind(this, userList1)">Select All</button>
          <button class"moveSelected" data-bind="click: moveAllFunction(userList1, userList2)">Move Selected</button>
          -->
      </div>
      <div class="list">
        <h2>Actual Things, Like For Real</h2>
        <ul multi-select="itemsThatAreReal" multi-select-function="selectItem" ui-sortable="sortableOptions" class="list list-container" ng-model="itemsThatAreReal"></ul>
        <!-- 

          <button class"moveSelected" data-bind="click: moveAllFunction(userList2, userList1)">Move Selected</button>
          <button class="selectAll" data-bind="click: selectAllItemsIn.bind(this, userList2)">Select All</button>
          -->
      </div>
    </div>
  </body>

</html>

angular.module('ui-sortable-multiselect', ['ui.sortable'])
  .directive('multiSelect', function() {
    return {
      restrict: 'AE',
      scope: {
        itemList: '=multiSelect',
        selectItem: '=multiSelectFunction'
      },
      templateUrl: 'list-item-template.html'
    }
  })
  .controller('MultiSelectDemo', ['$scope', function($scope) {

    $scope.actingOnThings = false;
    
    $scope.itemsInTrash = [
      { id: 1, name: "Test 1", selected: false },
      { id: 2, name: "Test 2", selected: false },
      { id: 3, name: "Test 3", selected: false }
    ];
    
    $scope.itemsThatAreReal = [
      { id: 4, name: "Test 4", selected: false },
      { id: 5, name: "Test 5", selected: false },
      { id: 6, name: "Test 6", selected: false }
    ];
    
    
    /*for(var i=4; i<7; i++) {
      $scope.itemsThatAreReal.push({
        id: i, name: "Test "+i, selected: false
      });
      console.log($scope.itemsThatAreReal);
    }*/
    
    $scope.lastArray = null;
    
    var moveTheseTo = function(items, from, to, atPosition) {
      //console.log(items, from, to, atPosition)
      
      if(!items.length) return;
      
      if(self.actingOnThings) return;
      
      self.actingOnThings = true;
      
      //copy things from one list to the next
      var copyFunction = function() {
        var newArgs = [atPosition, 0].concat(items);
        
        angular.forEach(items, function(item) {
          from.splice(from.indexOf(item), 1);
          item.selected = false;
        });
        
        console.log(to);
        to.splice.apply(to, newArgs);
        console.log(to);
        self.actingOnThings = false;
        console.log("no longer acting on things",items.length);
        
        $scope.$apply();
      }
      
      
      if(items.length > 300) {
        setTimeout(copyFunction, 100);
      } else {
        copyFunction();
      }
    };
    
    $scope.sortableOptions = {
      revert: 100,
      tolerance: "pointer",
      distance: 15,
      connectWith: ".list-container",
      update: function(event, ui) {

        //prevent an error
        if(typeof ui.item.scope() === 'undefined') return;
        
        var myList = ui.item.scope().itemList;
        var newList = $scope.$eval(ui.item.sortable.droptarget.attr('ng-model'));
        var newIndex = ui.item.sortable.dropindex;
        
        //get the currently active items
        var items = myList.filter(function(item) {
          return item.selected;
        });
        
        moveTheseTo(items, myList, newList, newIndex);
        
        //cancel drop, since I handle the movement myself
        ui.item.sortable.cancel();
      },
      helper: function(event, $item) {
        
        //select the unselected (if you're dragging something not previously selected)
        if(!$item.hasClass('selected')) {
          
          $item.parent().children().removeClass('selected');
          
          $item.scope().item.selected = true;
          $item.addClass('selected');
          
          angular.forEach($scope.lastArray, function(item) {
            item.selected = item.id == $item.data('id');
          });
        }
        
        // show "you have x items selected" if x > 1
        var $selected = $item.parent().find('.selected');
        var $helper;
        if ($selected.size() > 1) {
          $helper = $('<li class="item selected">You have ' + $selected.size() + ' items selected.</li>');
          $selected.removeClass('selected');
        } else {
          $helper = $selected;
        }

        return $helper;
      }
    };
    
    $scope.selectItem = function(event, array, $data) {
      $scope.lastArray = array;

      //default selection
      if (!event.ctrlKey && !event.metaKey && !event.shiftKey) {
        $data.selected = true
        angular.forEach(array, function(item) {
          if (item !== $data) {
            item.selected = false
          }
        });
        
      //shift-key selection
      } else if (event.shiftKey && !event.ctrlKey && $scope._lastSelectedIndex > -1) {
        var myIndex = array.indexOf($data);
        if (myIndex > $scope._lastSelectedIndex) {
          for (var i = $scope._lastSelectedIndex; i <= myIndex; i++) {
            array[i].selected = true;
          }
        } else if (myIndex < $scope._lastSelectedIndex) {
          for (var i = myIndex; i <= $scope._lastSelectedIndex; i++) {
            array[i].selected = true;
          }
        }
  
      //ctrl key selection
      } else if (event.ctrlKey && !event.shiftKey) {
        $data.selected = !$data.selected; 
      }
      
      //keep track of the index for shift-key stuff
      $scope._lastSelectedIndex = array.indexOf($data);
    };
  }]);

.item {
    cursor: pointer;
    padding-left: 10px;
}

.item.selected {
    background-color:#1e91fc;
    color: #fff;
    cursor: move;
}

div.list {
    display: inline-block;
    vertical-align: top;
    width: 300px;
    max-width: 350px;
}

div.list ul.list {
    max-height: 400px;
    height: 400px;
    overflow-y: scroll;
}

.list {
    padding: 0;
    background-color: #fafafa;
    border: 1px solid #CCC;
    list-style-type: none;
    margin: 0;
}

.list-manager {
  position: relative;
  display: table-cell;
}

.loading-overlay {
  position: absolute;
  line-height: 400px;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color:#fafafa;
  filter:alpha(opacity=90);
  opacity:0.9;
  -moz-opacity:0.9;
  z-index:100;

  text-align:center;
  vertical-align:middle;
}
<li class="item" 
    ng-repeat="item in itemList track by item.id"
    ng-click="selectItem($event, itemList, item)"
    ng-class="{selected: item.selected}"
    data-id="{{item.id}}"
>{{ item.name }}</li>