<!DOCTYPE html>
<html>

  <head>
    <link data-require="bootstrap-css@3.1.*" data-semver="3.1.1" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" />
    <link data-require="jqueryui@1.10.0" 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="jquery@*" data-semver="2.1.3" src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
    <script data-require="jqueryui@1.10.0" data-semver="1.10.0" src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.10.0/jquery-ui.js"></script>
    <script data-require="angular.js@*" data-semver="1.4.0-beta.5" src="https://code.angularjs.org/1.4.0-beta.5/angular.js"></script>
    <script data-require="ui-bootstrap@*" data-semver="0.12.0" src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.12.0.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-sortable/0.13.3/sortable.min.js"></script>
    <!--script src="sortable.js"></script-->
    <link rel="stylesheet" href="style.css" />
    <script src="app.js"></script>
    <script src="directives.js"></script>
  </head>

  <body ng-app="app" ng-controller="MainController as mainCtrl">
    <div class="content">
      <h2>Sortable Widgets</h2>
      <div class="row">
        <div class="col-md-12">
          
          <widget-container class="col-md-8">
          </widget-container>
          
        </div>
      </div>
    </div>
  </body>

</html>
/* Styles go here */

body {
  margin: 5px;
}

.feedback {
  margin: 10px;
  padding: 2px;
  font: .75em Courier, sans-serif;
}

.widget-container {
  margin: 5px;
  padding: 2px;
  border: 1px solid #ccc;
}

.widget-item {
  margin: 1px;
  border: 1px solid #ccc;
  padding: 5px;
}

.widget-placeholder {
  margin: 1px;
  background-color: #fbfbfb;
  height: 3px;
}

.widgets-drag-area {
  margin-top: 15px;
}

.handle {
  cursor: pointer;
}
#Sortable Dynamic Collection#

Custom directive with [ui-sortable](https://github.com/angular-ui/ui-sortable)
and `ng-repeat` for the items in the collection.

Author: [jasenhk](https://github.com/jasenhk)
(function() {
  var app = angular.module("app", ["ui.sortable"]);
  
  app.controller("MainController", [MainController]);
  
  function MainController() {
    
  }
  
})();
(function() {
  var app = angular.module("app");
  
  app.directive("widgetContainer", [WidgetContainer]);
  
  function WidgetContainer() {
    return {
      restrict: "E",
      replace: true,
      templateUrl: "widgetContainer.html",
      controller: "WidgetContainerController as widgetContainerCtrl",
      scope: { },
      link: function(scope, element, attrs, ctrl) {
        
      }
    };
  }
  
  app.controller("WidgetContainerController", ["$scope", "$timeout", "Widget", WidgetContainerController]);
  
  function WidgetContainerController($scope, $timeout, Widget) {
    var _this = this;
    this.$scope = $scope;
    this.$timeout = $timeout;
    this.widgetFactory = Widget;
    this.widgets = [];
    this.index = this.widgets.length;
    this.sortableOptions = {
      handle: "> .handle",
      update: function(event, ui) {
        _this.reorder();
      }
    };
  }
  
  WidgetContainerController.prototype.reorder = function() {
    var _this = this;
    this.$timeout(function() {
      for (var i = 0; i < _this.widgets.length; i++) {
        _this.widgets[i].pos = i;
      }
    }, 0);
  };
  
  WidgetContainerController.prototype.add = function() {
    var id = this.index;
    var pos = this.index;
    this.widgets.push(this.widgetFactory.create(id, pos));
    this.index++;
    console.log("add", this.widgets);
    this.reorder();
  };
  
  WidgetContainerController.prototype.remove = function(widget) {
    for (var i = 0; i < this.widgets.length; i++) {
      if (this.widgets[i].id == widget.id) {
        this.widgets.splice(i, 1);
        console.log("remove", this.widgets[i], this.widgets);
        this.reorder();
        return;
      }
    }
    this.reorder();
  };
  
  app.directive("widgetItem", [WidgetItem]);
  
  function WidgetItem() {
    return {
      restrict: "A",
      require: "^widgetContainer",
      scope: {
        widget: "="
      },
      controller: "WidgetItemController as widgetItemCtrl",
      link: function(scope, element, attrs, ctrl) {
        
      }
    };
  }
  
  app.controller("WidgetItemController", ["$scope", WidgetItemController]);
  
  function WidgetItemController($scope) {
    this.widget = $scope.widget;
  }
  
  app.factory("Widget", [function() {
    return Widget;
  }]);
  
  function Widget(id, pos) {
    this.id = id;
    this.pos = pos;
  }
  
  Widget.create = function(id, pos) {
    return new Widget(id, pos);
  };
  
})();
 
<div class="widget-container">
  <div class="row">
    <div class="col-xs-2">
      <button ng-click="widgetContainerCtrl.add()" class="btn btn-sm btn-success">Add Widget</button>    
    </div>
    <div class="col-xs-10" ng-show="widgetContainerCtrl.widgets.length > 0">
      <span class="feedback">{{widgetContainerCtrl.widgets}}</span>
    </div>
  </div>
  
  <div class="row" ng-show="widgetContainerCtrl.widgets.length > 0">
    <div class="col-xs-offset-1 col-xs-10 widgets-drag-area">
    
    <div ui-sortable="widgetContainerCtrl.sortableOptions" ng-model="widgetContainerCtrl.widgets">
      <div widget-item ng-repeat="widget in widgetContainerCtrl.widgets" class="widget-item"
        widget="widget"
        data-id="{{widget.id}}" data-pos="{{widget.pos}}">
        <span class="handle btn btn-xs btn-default">
          <i class="glyphicon glyphicon-move"></i>
        </span>
        <button class="btn btn-xs btn-danger" ng-click="widgetContainerCtrl.remove(widget)">
          <i class="glyphicon glyphicon-trash"></i>
        </button>
        <span class="label label-info">{{widget.id}}</span>
        <span class="label label-warning">{{widget.pos}}</span>
        <span>I'm the widget, gotta Love me!</span>
      </div>
    </div>
    
    </div>
  </div>
</div>