AngularJS cnSortable

Simple wrapper of jQuery UI sortable for AngularJS.

by chucknelson

HTML

<script src="http://code.jquery.com/jquery-1.10.0.min.js"></script>
<script src="http://code.jquery.com/ui/1.10.4/jquery-ui.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js"></script>
<div id="mainContainer" ng-app="angularTest" ng-controller="MainCtrl">
    <h1>Items</h1>
    <ul cn-sortable on-sort="orderItems(itemUpdates)">
        <li ng-repeat="item in items">
            <input type="hidden" name="id" value="{{item.id}}" />
            {{item.name}} | <a href="#" ng-click="deleteItem($index)">(delete)</a>
        </li>
    </ul>
    <ul>
        <li>
            <form name="theForm" ng-submit="formSubmit()">
                <label for="myName">New Item:</label>
                <input name="myName" type="text" ng-model="newName" required />
                <input name="submit" type="submit" value="Add Item" />
            </form>
        </li>
    </ul>
    
    <button name="reset" ng-click="reset()">Reset</button>
    
    <div class="debugText">
        Debug text in scope:<br /> 
        {{debugText}}
    </div>
    <div class="debugText">
        Last itemUpdates():<br />
        {{itemUpdatesText}}
    </div>
    
    <p class="footerText">AngularJS {{angularVersionText}}</p>
</div>

CSS

.debugText {
    margin: 10px 0;
    padding: 5px;
    background-color: #eee;
    border: 1px solid #ccc;
}

.footerText {
    font-size: .8em;
    color: #888;
}

input.ng-dirty.ng-invalid {
   border: 2px solid red;
}

JavaScript

'use strict';

var app = angular.module('angularTest', []);

app.controller('MainCtrl', ['$scope', 'filterFilter', 'orderByFilter', function($scope, filterFilter, orderByFilter) {
    var nextId;
    
    $scope.angularVersionText = angular.version.full + ', "' + angular.version.codeName + '"';
    
    $scope.initItems = function() {
        $scope.items = [
            {id: 1, name: 'Item 1', sort_order: 0},
            {id: 2, name: 'Item 2', sort_order: 1},
            {id: 3, name: 'Item 3', sort_order: 2}
        ];
        nextId = $scope.items.length + 1;
    };
    
    $scope.reset = function() {
        $scope.initItems();
        $scope.itemUpdatesText = '';
    };

    $scope.formSubmit = function() {
        var newItem = {id: nextId, name: $scope.newName, sort_order: $scope.items.length};
        $scope.items.push(newItem);
        
        nextId++;
        $scope.newName = '';
        $scope.theForm.$setPristine();
    };
    
    //method in controller that takes itemUpdates from cnSortable and does something with them
    //in this specific case we're using them to update each item in our model and then we sort the model by sort_order
    $scope.orderItems = function(itemUpdates) {
        itemUpdates.forEach(function(i) {
            $scope.orderItem(i);
        });
        $scope.items = orderByFilter($scope.items, 'sort_order');
        
        //debug        
        $scope.itemUpdatesText = orderByFilter(itemUpdates, 'newSortOrder');
    };
    
    $scope.orderItem = function(itemUpdate) {        
        var itemToOrder = filterFilter($scope.items, {id: itemUpdate.id}).pop();
        itemToOrder.sort_order = itemUpdate.newSortOrder;
    };
    
    $scope.deleteItem = function(index) {
        $scope.items.splice(index, 1);
    };
        
    //debug text
    $scope.$watch('items', function() {
        $scope.debugText = $scope.items;
    });
    
    //initial controller init
    $scope.reset();
}]);

//somewhat generic sorting...