Skip to content
Advertisement

Draggable and droppable items with multiple lists and orders

I am trying to find the order of the items as they are dragged over to the new column. I am updating the column that the item lives in once its dragged over with AJAX. I am also getting everything in order with $(this).sortable('serialize'). when I put that into an alert. The problem I am having though is when I send the array to PHP, one of the items gets dropped in the list. I am guessing it has something to do with the way I am using serialize but I am not sure. Any help I can get would be greatly appreciated. Co-workers tell me I should just accept the limits of the code and live with it. I disagree and know that the order the item is placed is almost as important as what column the data lives in. I’m thinking I need two different events in the javascript. One for the drag between lists and one in case the user rearranges items that are in the div. Hoping someone can point me in the right direction.

The HTML I have

<div class="col-lg-12 text-center">
     <div class="col-md-3">
          <h3>Unsorted Items</h3>
           <ul id="UNSORTED" class="connectedSortable">
                <li id="item-1">Unsorted item 1 from DB</li>
                <li id="item-2">Unsorted item 2 from DB</li>
                <li id="item-3">Unsorted item 3 from DB</li>
                <li id="item-4">Unsorted item 4 from DB</li>
                <li id="item-5">Unsorted item 5 from DB</li>
           </ul>
     </div>
     <div class="col-md-3">
           <h3>ACCEPTED</h3>
           <ul id="ACCEPTED" class="connectedSortable">
                 <li id="item-6">Unsorted item 6 from DB</li>
                 <li id="item-7">Unsorted item 7 from DB</li>
                 <li id="item-8">Unsorted item 8 from DB</li>   
           </ul>
     </div>
     <div class="col-md-3">
           <h3>REJECTED</h3>
            <ul id="REJECTED" class="connectedSortable">
                <!-- empty to show drag to -->        
            </ul>
      </div>        
</div>

The Javascript

<script>
    $(function() {
        $( "#UNSORTED, #ACCEPTED, #REJECTED" ).sortable({
            connectWith: ".connectedSortable",
            receive: function(event, ui) {
                // The position where the new item was dropped
                var newIndex = ui.item.index();
                var sender = ui.sender.context.id;
                var receiver = this.id;
                var idNum = ui.item.context.id;
                var display_order = $(this).sortable('serialize');

                //this alerts the correct order
                alert(display_order);

                //this when uncommented alerts what item tranfered to and from
                //alert(idNum + ' Was Transfered from "' + sender + '" to "' + receiver + '".');

                //this tell the new order of the items the item was dragged to
                //alert(receiver + ' Order is ' + $(this).sortable('serialize'));
                var action = 'update_selection';

                $.ajax({
                    url: "index.php?action=" + action + "&item=" + idNum + "&selection=" + receiver + '&item-' + display_order,
                    success:function (data) {
                        $("#results").html(data).slideDown('2000');
                    }
                });

            },
            stop: function (event, ui) {
                var sender = this.id;
                var data = $(this).sortable('serialize');

                //this when uncommented alerts new order of old list
                //alert(sender + ' Order is ' + data);

                //this was to write new order of old list unless I can figure out how to write it in the 'receive' event
                /*$.ajax({
                 data: oData,
                 type: 'POST',
                 url: '/your/url/here'
                 });*/
            }
        }).disableSelection();
    });
</script>

Shortened version of PHP

$item_id = filter_input(INPUT_GET, 'item');

/*the number after item- is dynamic from the DB and I was unable to get         serialize to work without the item- in it so now I am removing item- to get the actual DB id with preg_replace */
$item_id = preg_replace('/^item-/', '', $item_id);

$selection = filter_input(INPUT_GET, 'selection');

//can't use filter_input on an array
$display = $_GET['item'];

/*this dumps the array with an array item missing. Sometimes its the first item in the array and sometimes its not */
var_dump($display);

Advertisement

Answer

Okay I figured it out. I needed to pass the AJAX with POST and not GET. BUT, I was still having a problem with the way I was doing it because I cannot do it the way I was doing it. I was intending on having a table that just had the display orders for each column. But that’s pretty dumb when in the item table there is already a column name that the item belongs to. Its easier to just add another column with display order that update whenever the item is moved to another column. So here is my full working code which updates with AJAX and remembers where in the new column the item was placed. If anyone ever comes across this post and knows a better way, please do share. I love learning from my mistakes.

The HTML

<div class="col-lg-12 text-center sortable">
     <div class="col-md-3">
          <h3>Unsorted Items</h3>
<!--I am not including the PHP loop to display the list of items -->
           <ul id="UNSORTED" class="sort-list">
                <li id="item-1">Unsorted item 1 from DB</li>
                <li id="item-2">Unsorted item 2 from DB</li>
                <li id="item-3">Unsorted item 3 from DB</li>
                <li id="item-4">Unsorted item 4 from DB</li>
                <li id="item-5">Unsorted item 5 from DB</li>
           </ul>
     </div>
     <div class="col-md-3">
           <h3>ACCEPTED</h3>
           <ul id="ACCEPTED" class="sort-list">
                 <li id="item-6">Unsorted item 6 from DB</li>
                 <li id="item-7">Unsorted item 7 from DB</li>
                 <li id="item-8">Unsorted item 8 from DB</li>   
           </ul>
     </div>
     <div class="col-md-3">
           <h3>REJECTED</h3>
            <ul id="REJECTED" class="sort-list">
                <!-- empty to show drag to -->        
            </ul>
      </div>        
</div>

The Javascript

<script>
    $(function(){

    /* Sort steps */
    $('.container').sortable({
        axis: "y",
        update: function (event, ui) {
            var data = $(this).sortable('toArray');
            $("#result").html("JSON:<pre>"+JSON.stringify(data)+"</pre>");
        }   
    }); 

    /* Here we will store all data */
    var myArguments = {};   

    function assembleData(object,arguments)
    {       
        var data = $(object).sortable('toArray'); // Get array data 
        var step_id = $(object).attr("id"); // Get step_id and we will use it as property name
        var arrayLength = data.length; // no need to explain

        /* Create step_id property if it does not exist */
        if(!arguments.hasOwnProperty(step_id)) 
        { 
            arguments[step_id] = new Array();
        }   

        /* Loop through all items */
        for (var i = 0; i < arrayLength; i++) 
        {
            var image_id = data[i]; 
            /* push all image_id onto property step_id (which is an array) */
            arguments[step_id].push(image_id);          
        }
        return arguments;
    }   

    /* Sort images */
    $('.sort-list').sortable({
        connectWith: '.sort-list',

                //leaves out the bootstrap class
        items : ':not(.col-md-3)',

        /* That's fired first */    
        start : function( event, ui ) {
            myArguments = {}; /* Reset the array*/  
        },      
        /* That's fired second */
        remove : function( event, ui ) {
            /* Get array of items in the list where we removed the item */          
            myArguments = assembleData(this,myArguments);
        },      
        /* That's fired thrird */       
        receive : function( event, ui ) {
            /* Get array of items where we added a new item */  
            myArguments = assembleData(this,myArguments);       
        },
        update: function(e,ui) {
            if (this === ui.item.parent()[0]) {
                 /* In case the change occures in the same container */ 
                 if (ui.sender == null) {
                    myArguments = assembleData(this,myArguments);       
                } 
            }
        },      
        /* That's fired last */         
        stop : function( event, ui ) {                  
            /* Send JSON to the server */
            var action = 'update_selection';
            var orders = JSON.stringify(myArguments);
            $.ajax({
                url: 'index.php',
                type: 'POST',
                data: {action: action, code: orders},

                //I used success function to var_dump PHP when testing
                success:function (data) {
                     $("#result").html(data).slideDown('2000');
                }
            });
        }   
    });
});
</script>

and last but not least the PHP file. I use MVC so I am calling the action and sending my script to the right case to process the PHP (Just in case someone reading this is unaware I will include that whole PHP file.)

require("classes/Db.class.php");

$db = new Db();

if (isset($_POST['action'])) {
    $action = $_POST['action'];
} else if (isset($_GET['action'])) {
    $action = $_GET['action'];
} else {
    $action = 'home';
}

switch ($action) {
    case 'home':
        //gets all items and display order for those items
        $unsorted = $db->query("SELECT * FROM sorting WHERE column_name = 'UNSORTED' ORDER BY display_order");
        $accepted = $db->query("SELECT * FROM sorting WHERE column_name = 'ACCEPTED' ORDER BY display_order");
        $rejected = $db->query("SELECT * FROM sorting WHERE column_name = 'REJECTED' ORDER BY display_order");
        $possible = $db->query("SELECT * FROM sorting WHERE column_name = 'POSSIBLE' ORDER BY display_order");

        include_once('home.php');
        break;

    case 'update_selection':

        $json = filter_input(INPUT_POST, 'code'); //gets the json stringify
        $array = json_decode($json, true); //specify an associative array instead of an object from json_decode

        foreach($array as $key => $value){

            //gets column item belongs to now
            $column_name = $key;

            foreach($value as $key => $number){
                //this gets the key which we will use for ordering
                $order = $key;

                //update DB with column name item belongs to and the new order of all items in that column
                $db->query("UPDATE sorting SET column_name = :column_name, display_order = :order WHERE gun_id = :number", array("column_name"=>$column_name, "number"=>$number, "order" => $order));
            }  
        }

        break;
User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement