Drag and drop sortable ng:repeats in AngularJS?

JavascriptAngularjsJquery Ui-Sortable

Javascript Problem Overview


Is it at all easy to use jQuery.sortable on ng-repeat elements in AngularJS?


It would be awesome if re-ordering the items automatically propagated that ordering back into the source array. I'm afraid the two systems would fight though. Is there a better way to do this?

Javascript Solutions


Solution 1 - Javascript

Angular UI has a sortable directive,Click Here for Demo

Code located at ui-sortable, usage:

<ul ui-sortable ng-model="items" ui-sortable-update="sorted">
  <li ng-repeat="item in items track by $index" id="{{$index}}">{{ item }}</li>
</ul>
$scope.sorted = (event, ui) => { console.log(ui.item[0].getAttribute('id')) }

Solution 2 - Javascript

I tried to do the same and came up with the following solution:

angular.directive("my:sortable", function(expression, compiledElement){
	return function(linkElement){
		var scope = this;
		linkElement.sortable(
		{
			placeholder: "ui-state-highlight",
			opacity: 0.8,
			update: function(event, ui) {
				var model = scope.$tryEval(expression);
				var newModel = [];
				var items = [];
				linkElement.children().each(function() {
					var item = $(this);
					// get old item index
					var oldIndex = item.attr("ng:repeat-index");
					if(oldIndex) {
						// new model in new order
						newModel.push(model[oldIndex]);
						// items in original order
						items[oldIndex] = item;
						// and remove
						item.detach();
					}
				});
				// restore original dom order, so angular does not get confused
				linkElement.append.apply(linkElement,items);

				// clear old list
				model.length = 0;
				// add elements in new order
				model.push.apply(model, newModel);

				// presto
				scope.$eval();

				// Notify event handler
				var onSortExpression = linkElement.attr("my:onsort");
				if(onSortExpression) {
					scope.$tryEval(onSortExpression, linkElement);
				}
			}
		});
	};
});

Used like this:

<ol id="todoList" my:sortable="todos" my:onsort="onSort()">

It seems to work fairly well. The trick is to undo the DOM manipulation made by sortable before updating the model, otherwise angular gets desynchronized from the DOM.

Notification of the changes works via the my:onsort expression which can call the controller methods.

I created a JsFiddle based on the angular todo tutorial to shows how it works: http://jsfiddle.net/M8YnR/180/

Solution 3 - Javascript

This is how I am doing it with angular v0.10.6. Here is the jsfiddle

angular.directive("my:sortable", function(expression, compiledElement){
    // add my:sortable-index to children so we know the index in the model
    compiledElement.children().attr("my:sortable-index","{{$index}}");

    return function(linkElement){
        var scope = this;            

        linkElement.sortable({
            placeholder: "placeholder",
            opacity: 0.8,
            axis: "y",
            update: function(event, ui) {
                // get model
                var model = scope.$apply(expression);
                // remember its length
                var modelLength = model.length;
                // rember html nodes
                var items = [];

                // loop through items in new order
                linkElement.children().each(function(index) {
                    var item = $(this);

                    // get old item index
                    var oldIndex = parseInt(item.attr("my:sortable-index"), 10);

                    // add item to the end of model
                    model.push(model[oldIndex]);

                    if(item.attr("my:sortable-index")) {
                        // items in original order to restore dom
                        items[oldIndex] = item;
                        // and remove item from dom
                        item.detach();
                    }
                });

                model.splice(0, modelLength);

                // restore original dom order, so angular does not get confused
                linkElement.append.apply(linkElement,items);

                // notify angular of the change
                scope.$digest();
            }
        });
    };
});

Solution 4 - Javascript

Here's my implementation of sortable Angular.js directive without jquery.ui :

Solution 5 - Javascript

you can go for ng-sortable directive which is lightweight and it does not uses jquery. here is link ng-sortable drag and drop elements

Demo for ng-sortable

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionNick RetallackView Question on Stackoverflow
Solution 1 - JavascriptGuillaume86View Answer on Stackoverflow
Solution 2 - JavascriptManuel WoelkerView Answer on Stackoverflow
Solution 3 - JavascriptrespectTheCodeView Answer on Stackoverflow
Solution 4 - JavascriptSebastien ChartierView Answer on Stackoverflow
Solution 5 - JavascriptDheeraj NalawadeView Answer on Stackoverflow