Angular Sortable Dynamic Collection

Here’s an example of a sortable (ui-sortable) on a dynamic collection that I’ve worked out. I’ve wrapped this in a custom directive for a collection of widgets that can be re-ordered.

See the Plnkr working example.

Screen Shot

It uses Angular, Bootstrap and ui-sortable. Unfortunately, there is a dependency on jQuery UI Sortable but I don’t see any problems with this example.


Custom directive

<widget-container class="widget-container"></widget-container>

Directive Template

This is the basic template with the important bits (most of the styling removed). There’s a second directive widget-item embedded to help with the child (collection) items.

<div class="widget-container">
    <button ng-click="widgetContainerCtrl.add()">Add Widget</button>
    <div ui-sortable="widgetContainerCtrl.sortableOptions" ng-model="widgetContainerCtrl.widgets">
      <div widget-item ng-repeat="widget in widgetContainerCtrl.widgets"
        data-id="{{}}" data-pos="{{widget.pos}}">
        <span class="handle">
          <i class="glyphicon glyphicon-move"></i>
        <button ng-click="widgetContainerCtrl.remove(widget)">
          <i class="glyphicon glyphicon-trash"></i>
        <span>I'm the widget, gotta Love me!</span>

We’ve got to be careful with the DOM structure to satisfy ui-sortable — the ng-repeat needs to be directly below ui-sortable.

Directive JavaScript

  var app = angular.module("app", ["ui.sortable"]);
  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", WidgetContainerController]);
  function WidgetContainerController($scope, $timeout) {
    var _this = this;
    this.$scope = $scope;
    this.$timeout = $timeout;
    this.widgets = [];
    this.sortableOptions = {
      handle: "< .handle",
      update: function(event, ui) {

  WidgetContainerController.prototype.add = function() {
    // this.widgets.push()
  WidgetContainerController.prototype.remove = function(widget) {
    // this.widgets.splice()
  WidgetContainerController.prototype.reorder = function() {
    // reassign widget.pos

Here’s the child directive:

  app.directive("widgetItem", [WidgetItem]);
  function WidgetItem() {
    return {
      restrict: "A",
      scope: {
        widget: "=";
      controller: "WidgetItemController as widgetItemCtrl",
      link: function(scope, element, attrs, ctrl) { }
  app.controller("WidgetItemController", ["$scope", WidgetItemController]);
  function WidgetItemController($scope) {
    this.widget = $scope.widget;
  WidgetItem.prototype.doSomething = function() { }