This directive allows you to sort an array with drag & drop.
- JQuery
- JQueryUI 1.9+
- AngularJS
Single minified cdn link ~245kB and example with JQuery v1.x, required parts of JQueryUI v1.10, AngularJS v1.2 & latest angular-ui-sortable.
Notes:
- JQuery must be included before AngularJS.
- JQueryUI dependecies include core, widget, mouse & sortable. Creating a custom build will greatly reduce the required file size. (CDN links for comparison: full vs minimal)
Load the script file: sortable.js in your application:
<scripttype="text/javascript" src="modules/directives/sortable/src/sortable.js"></script>Add the sortable module as a dependency to your application module:
varmyAppModule=angular.module('MyApp',['ui.sortable'])Apply the directive to your form elements:
<ului-sortableng-model="items"><ling-repeat="item in items">{{item }}</li></ul>Developing Notes:
ng-modelis required, so that the directive knows which model to update.ui-sortableelement should only contain oneng-repeatand not any other elements (above or below).
Otherwise the index matching of the generated DOM elements and theng-model's items will break.
In other words: The items ofng-modelmust match the indexes of the generated DOM elements.ui-sortablelists containing many 'types' of items can be implemented by using dynamic template loading with ng-include or a loader directive, to determine how each model item should be rendered. Also take a look at the Tree with dynamic template example.
All the jQueryUI Sortable options can be passed through the directive.
Additionally, the ui argument of the available callbacks gets enriched with some extra properties as specified to the API.md file.
myAppModule.controller('MyController',function($scope){$scope.items=["One","Two","Three"];$scope.sortableOptions={update: function(e,ui){ ... },axis: 'x'};});<ului-sortable="sortableOptions" ng-model="items"><ling-repeat="item in items">{{item }}</li></ul>When using event callbacks (start/update/stop...), avoid manipulating DOM elements (especially the one with the ng-repeat attached). The suggested pattern is to use callbacks for emmiting events and altering the scope (inside the 'Angular world').
To have a smooth horizontal-list reordering, jquery.ui.sortable needs to detect the orientation of the list. This detection takes place during the initialization of the plugin (and some of the checks include: whether the first item is floating left/right or if 'axis' parameter is 'x', etc). There is also a known issue about initially empty horizontal lists.
To provide a solution/workaround (till jquery.ui.sortable.refresh() also tests the orientation or a more appropriate method is provided), ui-sortable directive provides a ui-floating option as an extra to the jquery.ui.sortable options.
<ului-sortable="{'ui-floating': true }" ng-model="items"><ling-repeat="item in items">{{item }}</li></ul>OR
$scope.sortableOptions={'ui-floating': true};<ului-sortable="sortableOptions" ng-model="items"><ling-repeat="item in items">{{item }}</li></ul>ui-floating (default: undefined)
Type: Boolean/String/undefined
- undefined: Relies on jquery.ui to detect the list's orientation.
- false: Forces jquery.ui.sortable to detect the list as vertical.
- true: Forces jquery.ui.sortable to detect the list as horizontal.
- "auto": Detects on each drag
startif the element is floating or not.
Inside the update callback, you can check the item that is dragged and cancel the sorting.
$scope.sortableOptions={update: function(e,ui){if(ui.item.scope().item=="can't be moved"){ui.item.sortable.cancel();}}};Notes:
updateis the appropriate place to cancel a sorting, since it occurs before any model/scope changes but after the DOM position has been updated. Soui.item.scopeand the directive'sng-model, are equal to the scope before the drag start.- To cancel a sorting between connected lists,
cancelshould be called inside theupdatecallback of the originating list.
Single sortabledemo
start activate /* multiple: sort/change/over/out */ beforeStop update <= call cancel() here if needed deactivate stop Connected sortablesdemo
list A: start list B: activate list A: activate /* both lists multiple: sort/change/over/out */ list A: sort list A: change list B: change list B: over list A: sort list B: out list A: sort list A: beforeStop list A: update <= call cancel() here if needed list A: remove list B: receive list B: update list B: deactivate list A: deactivate list A: stop For more details about the events check the jQueryUI API documentation.
- Simple Demo
- Connected Lists
- Filtering (details)
- Ordering 1 & Ordering 2 (details)
- Cloning (details)
- Horizontal List
- Tree with dynamic template
- Canceling
- Draggable Handle
The above pen's are provided as a good starting point to demonstrate issues, proposals and use cases. Feel free to edit any of them for your needs (don't forget to also update the libraries used to your version).
We use Karma and jshint to ensure the quality of the code. The easiest way to run these checks is to use grunt:
npm install -g grunt-cli npm install && bower install gruntThe karma task will try to open Firefox and Chrome as browser in which to run the tests. Make sure this is available or change the configuration in test\karma.conf.js
We have one task to serve them all !
grunt serveIt's equal to run separately:
grunt connect:server: giving you a development server at http://127.0.0.1:8000/.grunt karma:server: giving you a Karma server to run tests (at http://localhost:9876/ by default). You can force a test on this server withgrunt karma:unit:run.grunt watch: will automatically test your code and build your demo. You can demo generation withgrunt build:gh-pages.
