How to set up Sortable in AngularJS – without jQuery!

By | February 18, 2014

The de-facto standard of sortable libraries is jQuery UI Sortable, but it’s a bit silly to use jQuery and jQuery UI when you’re already using AngularJS. Why should you have to include two other big asset files for one, small functionality?

Here’s an alternative solution using this sortable library instead.

First off, here’s the demo on Plunker.

There are detailed comments throughout the code, but here’s the basic gist of it:

<!-- html / view -->
<div ng-app="myApp">
<div ng-controller="SortController" class="container">
    <ul id="my-sort" class="sortable" ng-model="people">
        <!-- note the "track by" section. this is important so that it doesn't rebuild the list -->
        <li ng-repeat="person in people track by person.id" data-index="{{$index}}">
            {{person.id}} - {{person.name}}
        </li>
    </ul>
</div>
</div>
// controllers.js
var myApp = angular.module('myApp',[]);
myApp.controller('SortController', ['$scope', '$http', function($scope, $http) {
    
  // initialize data. you would normally use a $http.get() here
  $scope.newSortIndexes = [];   
  $scope.people = [
    { id: 3, name: 'john' },
    { id: 4, name: 'mary' },
    { id: 1, name: 'james' },
    { id: 2, name: 'nancy' }
  ];
  
  // add sortable to <ul>
  var mySort = document.getElementById("my-sort");
  new Sortable(mySort, {
    onUpdate: function (evt){
      // get new sort order based on indexes
      var newSortIndexes = [];
      var liElements = mySort.getElementsByTagName("li");
      for (var i=0; i<liElements.length; i++) {
        newSortIndexes.push(liElements[i].getAttribute('data-index'));
      }
      
      // process change. you would normally use a $http.post() here
      $scope.newSortIndexes = newSortIndexes;
      $scope.people = getSorted($scope.people, newSortIndexes);
      $scope.$apply();
    }
  });   
}]);

// sort by indexes
// http://stackoverflow.com/q/4046967/3261678
function getSorted(arr, sortArr) {
  var result = [];
  for(var i=0; i<arr.length; i++) {
    result[i] = arr[sortArr[i]];
  }
  return result;
}

3 thoughts on “How to set up Sortable in AngularJS – without jQuery!

  1. Denis

    // controllers.js
    var myApp = angular.module(‘myApp’,[]);
    myApp.controller(‘SortController’, [‘$scope’, ‘$http’, function($scope, $http) {

    // initialize data. you would normally use a $http.get() here
    $scope.newSortIndexes = [];
    $scope.people = [
    { id: 3, name: ‘john’, sort: 0 },
    { id: 4, name: ‘mary’, sort: 1 },
    { id: 1, name: ‘james’, sort: 2 },
    { id: 2, name: ‘nancy’, sort: 3 }
    ];

    // add sortable to
    var mySort = document.getElementById(“my-sort”);
    new Sortable(mySort, {
    onUpdate: function (evt){

    angular.forEach($scope.people, function(value, key) {
    value.sort = evt.item.parentElement.children[key].getAttribute(‘data-index’);
    });

    $scope.people = $filter(‘orderBy’)($scope.people, ‘sort’);
    }
    });
    }]);

    Reply
  2. Pragun Bhutani

    Hello Amnah,

    Thanks for sharing your solution! I’ve been trying to implement a sortable image gallery using the same sortable library that you’ve mentioned and I used data attributes as well.
    I’ve been wondering though, isn’t there a way to achieve the same using Angular’s data binding??

    Thanks,
    Pragun

    Reply
  3. Sergey Petrenko

    for a longer lists > 10, this becomes buggy. The fix is to move
    $scope.people = getSorted($scope.people, newSortIndexes);
    line into the $apply function. Then it works like a charm.
    Here is how it should look like:
    $scope.$apply( function() {
    $scope.people = getSorted($scope.people, newSortIndexes);
    });

    Thanks for your code through – it saved my day 🙂

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *