Wednesday, March 13, 2013

jQuery UI: Preserving Sortables

The sortable interaction widget let's the user arrange a list of items. Which is great, except for when you want to preserve the order of those items. As soon as you reload the page, the order is gone. That is, of course, unless you're storing the order as part of the application somewhere. But if you just want to create a sortable that preserves the order using the cookie plugin, here is how you can do that. This is my basic sortable list.

<ul id="preserved-sortable">
    <li id="doc1" class="ui-state-default">
        <span class="ui-icon ui-icon-document"></span>
        <span>Doc 1</span>
    </li>
    <li id="doc2" class="ui-state-default">
        <span class="ui-icon ui-icon-document"></span>
        <span>Doc 2</span>
    </li>
    <li id="doc3" class="ui-state-default">
        <span class="ui-icon ui-icon-document"></span>
        <span>Doc 3</span>
    </li>
    <li id="doc4" class="ui-state-default">
        <span class="ui-icon ui-icon-document"></span>
        <span>Doc 4</span>
    </li>
</ul>

Next, I turn it into a sortable widget. Except, I have to make some changes to the sortable widget itself. Basically, I just want to give the sortable a preserve option. When true, it'll use the ID of the widget as the cookie name, and will store the order of the item IDs.

(function( $, undefined ) {

$.widget( "ab.sortable", $.ui.sortable, {

    // New option to preserve the sort order
    options: {
        preserve: false
    },

    _create: function() {

        // Don't do anything special if preserve is false
        if ( !this.options.preserve ) {
            return this._super();
        }

        // Load the sortable items, and the preserved order
        var element = this.element,
            items = this._getItemsAsjQuery(),
            preserved = $.cookie( element.attr( "id" ) );

        // If a cookie was found, sort the itmes.
        if ( preserved ) {
            
            preserved = preserved.split( "," );

            items = items.sort( function( a, b ) {
                var a_id = $( a ).attr( "id" ),
                    b_id = $( b ).attr( "id" ),
                    a_index = preserved.indexOf( a_id ),
                    b_index = preserved.indexOf( b_id );
                return a_index > b_index;
            });

            items.appendTo( element );

        }

        // Business as usual
        this._super();

        // Update the cookie when the order changes
        this._on( element, {
            sortableupdate: function( e, ui ) {
                $.cookie(
                    $( element ).attr( "id" ),
                    this.toArray().toString()
                );
            }
        });
        
    },

});

})( jQuery );

// Create the sortable using the new option
$(function() {
    $( "#preserved-sortable" ).sortable({
        preserve: true
    });
});

Now when I start re-arranging these items, the cookie value is updated. When the widget is created on page load, it knows to look for that cookie and to sort the items in order to restore the sorted order. For example, the cookie value doc2,doc3,doc4,doc1 would cause the sortable to look like this on page load.