angular-ui-tree is a tree component similar to jQuery UI's sortable and draggable functionality. Whilst the ui-sortable directive attempts to provide Angular.js bindings for jQuery UI sortable, the angular-ui-tree directive doesn't have jQuery or jQuery UI as a dependency. Implementing a nested sortable / draggable list is pretty easy. First off, install with bower:
bower install angular-ui-tree
Unfortunately, even using Yeoman, `bower install angular-ui-tree --save-dev` didn't insert the references into my `index.html` file for some reason so you'll have to do it manually. Add the CSS file to the `<head>`:
<link rel="stylesheet" href="bower_components/angular-ui-tree/dist/angular-ui-tree.min.css">
And add the `<script>` tag to the bottom of the `index.html` page:
<script type="text/javascript" src="bower_components/angular-ui-tree/dist/angular-ui-tree.js"></script>
And inject the dependency into your module:
var app = angular.module('testApp', [
Once this is done, create a new controller and view using Yeoman via the command line:
yo angular:controller nestable
yo angular:view nestable
Inside of your `nestable.html` view, your boilerplate nested sortable code will look something like this:
<div ui-tree="treeOptions">
  <ol ui-tree-nodes="" ng-model="base.Items">
    <li ng-repeat="group in base.Items" ui-tree-node data-type="{{group.type}}">
      <div ui-tree-handle>
      <ol ui-tree-nodes="" ng-model="group.Documents">
        <li ng-repeat="subItem in group.Documents" ui-tree-node data-type="{{subItem.type}}">
          <div ui-tree-handle>
The ui-tree directive takes a `treeOptions` object which you should setup in your `nestable` controller. the `treeOptions` callback has many different options; various callbacks related to the state of nested list including `beforeDrag` (this one is obvious), `accept` (callback that is called to check if an item can be dragged to its target node), and `dropped` (triggered when an item has passed `accept` validation and is dropped to the target node). The controller code will look something like this:
$scope.treeOptions = {
    beforeDrag: function(sourceNodeScope) {
        if ($rootScope.globals.currentUser.isAdmin) {
            return true;
        return false;
    accept: function(sourceNodeScope, destNodesScope, destIndex) {
        // The source of the element being dragged
        var srcId = sourceNodeScope.$modelValue.Id;

        // The destination node where the element is being dragged to
        var destId = destNodesScope.$modelValue[0].Id;

        // Check that the src and destination ids are the same (they have to be)
        if (srcAppendixId === destAppendixId) $scope.acceptable = true;

        return $scope.acceptable;
    dropped: function(event) {

        if ($scope.acceptable) {
            var srcNode = event.source.nodeScope;
            var destNodes = event.dest.nodesScope;
            var srcId = srcNode.$modelValue.Id;
            var success = false;

                if (srcId !== undefined) {

                    // do stuff
            // Reset acceptable marker to falsey for next drag between appendices
            $scope.acceptable = false;
And that's it. The `ng-model` for the outer and inner list is entirely up to you!