UI.Sortable directive
This directive allows you to sort an array with drag & drop.
Requirements
- JQuery
- JQueryUI (1.9+)
- AngularJS
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)
Usage
Load the script file: sortable.js in your application:
<script type="text/javascript" src="modules/directives/sortable/src/sortable.js"></script>
Add the sortable module as a dependency to your application module:
var myAppModule = angular.module('MyApp', ['ui.sortable'])
Apply the directive to your form elements:
<ul ui-sortable ng-model="items">
<li ng-repeat="item in items">{{ item }}</li>
</ul>
Developing Notes:
ng-model
is required, so that the directive knows which model to update.ui-sortable
element should only contain oneng-repeat
and 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-model
must match the indexes of the generated DOM elements.ui-sortable
lists 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.
Options
All the jQueryUI Sortable options can be passed through the directive.
myAppModule.controller('MyController', function($scope) {
$scope.items = ["One", "Two", "Three"];
$scope.sortableOptions = {
update: function(e, ui) { ... },
axis: 'x'
};
});
<ul ui-sortable="sortableOptions" ng-model="items">
<li ng-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').
Canceling
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:
update
is 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.scope
and the directive'sng-model
, are equal to the scope before the drag start.- To cancel a sorting between connected lists,
cancel
should be called inside theupdate
callback of the originating list.
jQueryUI Sortable Event order
Single sortable demo
start
activate
/* multiple: sort/change/over/out */
beforeStop
update <= call cancel() here if needed
deactivate
stop
Connected sortables demo
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.
Examples
- Simple Demo
- Connected Lists & Connected Lists Canceling
- Filtering (details)
- Ordering 1 & Ordering 2 (details)
- Cloning (details)
- Tree with dynamic template
Reporting Issues
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).
Testing
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
grunt
The 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
Grunt Serve
We have one task to serve them all !
grunt serve
It'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
.