1

I am creating an angularjs app that also uses jquery ui sortable. I'm looking for a clean way to update a "SortOrder" property on my models based on their position on the page, every time a sort is performed.

Here's what I have so far:

<div class="object-container" sortable>
    <div ng-repeat="object in objects">
        <div class="obj-title">{{object.Title}}</div>
    </div>
</div>

And here is my sortable directive:

myApp.directive('sortable', function(){
    return {
        restrict : 'A',
        link : function(scope, element, attributes){
            element.sortable({
                helper : 'clone',
                stop : function(event, ui){
                    scope.syncOrder();
                }
            });
        }
    };
});

And my "syncOrder" function:

$scope.syncOrder = function(){
    // some speculation here:
    for(var i in $scope.objects){
        $scope.objects[i].SortOrder = ???;
    }
};

The SortOrder property exists and is set on page load, but obviously they need to be updated upon sorting, so I can pass the updated set back to the server and save. Any ideas?

Thanks!

1
  • 1
    why don't you have a look at ui-sortable Commented Jul 18, 2014 at 4:08

1 Answer 1

7

First, we need a way to tie the elements to the objects in our model. We can do this by giving our elements in the repeater an ID and grabbing that ID when the sort stops.

<ul sortable>
    <li id="elem-{{obj.id}}" ng-repeat="obj in objects">
        { obj.message }}
    </li>
</ul>

Now in our directive we need to use the sortable's toArray method (documentation here) to get a list of all element IDs and their positions and we need to pass that into our syncOrder function.

app.directive('sortable', function ($timeout) {
  return function (scope, element, attributes) {
    element.sortable({
        stop: function(event, ui){
            scope.syncOrder(element.sortable('toArray'));
        }
    });
  };
});

Now we can modify our syncOrder function to iterate over all objects in our model, then iterate over all elements in the array passed in by sortable, compare the IDs, and update the model to reflect the new positions.

$scope.syncOrder = function (elemPositions) {
    // loop over our model objects
    $scope.objects.forEach(function (obj) {
        // loop over all the element positions
        elemPositions.forEach(function (elemId, index) {
            // shave off the string part of the ID on the element.
            var id = parseInt(elemId.replace(/elem-/, ''));

            // Items in the elemPositions array are in the order that they
            // are on the page. If the element ID matches the ID we gave to
            // our object, update the sort order to match the current index.
            if (id === obj.id) {
                obj.sortOrder = index;
            }
        });
    });
};

Once we have that setup there's one final thing we need to do. The stop event from the jquery sortable widget is executed outside of the angular execution block. In other words, angular has no way to know exactly if or when the sortable is going to update its objects array on our $scope. Because of this, your view will appear to not update when you sort; though if you iterate over your array you would see the array did indeed update.

We need to force angular to be aware that changes are about to happen to objects in scope. To do this we'll call scope.$apply in our directive.

return function (scope, element, attributes) {
    element.sortable({
        stop: function(event, ui){
            // Without this, angular magic won't happen when the objects array is updated.
            scope.$apply(function () {
                scope.syncOrder(element.sortable('toArray'));
            });
        }
    });
};

Working Demo

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.