Topic: Found a drag/drop demo from Nathan Smith, but how to save?

I am looking quite long for a solution for this. I found a great demo by Nathan Smith, using jQuery and interface to drag and drop favourites into a list. But how can I save these two lists (it doesn't work on the demo). Can I get the missing code for this? I would be very gratefull.

I did manage to get both lists loaded from a database, but I also want to save the order after drag and dropping the items. It has to save BOTH lists when pressing the save button, cause if only 1 list is saved (the one on the right) the left will reset and I don't want that.

Help help help smile

The demo is here:
http://host.sonspring.com/dragdrop/

Many thx

Re: Found a drag/drop demo from Nathan Smith, but how to save?

I don't know how jQuery works, but here's what I would do here.  Have an HTML form with the submit button (like you already do apparently) and have two hidden form elements, one for each list.  Each time a list is re-ordered have jQuery update the value of the appropriate hidden element, with ID numbers separated by commas or something like that.  PHP or whatever can then remove the commas as save it to your database.  Does that make sense? 

<input type="hidden" name="list1" value="12,34,19,3,41" />
<input type="hidden" name="list2" value="4,5,1,3,7" />
<input type="submit" value="Save Lists!" />

Re: Found a drag/drop demo from Nathan Smith, but how to save?

it just so happens that the jQuery interface plugin (http://interface.eyecon.ro/demos) does all that work for you - forget updating some hidden field, you can use SortSerialize.  You can see SortSerialize in action here: http://interface.eyecon.ro/demos/sort.html (I'm pretty sure Nathan's demo is based on this, though I could be wrong).  In the eyecon demo notice what is alerted out when you click on one of the "serialize..." links at the bottom. It returns a string containing the current order of the list ID's, pre-formatted as a POST string for your programming enjoyment. Then all you have to do is send that to a PHP function somewhere and have it put that list into your DB. The following is how I do it for one part of the CMS I'm building:

			$('ul.sorter').Sortable({
					accept : 		'sortableitem',
					helperclass : 	'sorthelper',
					activeclass : 	'sortableactive',
					hoverclass : 	'sortablehover',
					opacity: 		0.8,
					fx:				150,
					axis:			'vertically',
					opacity:		0.4,
					revert:			true,
					onStop : function()
					{
						serial = $.SortSerialize('sorter');
						var data = serial.hash + "&tp_id=" + val;
						//alert(data);
						update_col(data, "col_order");
					}
			});

onStop is where the magic happens. This function is triggered when the user "drops" whatever list item he/she was dragging. You set the variable serial to teh POST string you want to send to PHP. The thing to notice here is that it will serialize whichever list whose ID is sent to it. Therefore you can have two lists with id="list1" and id="list2" and you can access the serial hash for each one by calling $.SortSerialize('list1'); and $.SortSerialize('list2'); and getting each serial.hash. It's pretty simple - probly easier than it sounds.

Once you have this POST string for each column, all you have to do is write a PHP function to loop through it and update the order value for each list item. Say the POST variable is called $ord. Your PHP would be like

		$count = 50;
		foreach($ord as $o)
		{
			$this->update_order($o, $count);
			$count += 50;
		}

where update_order is a query to set each row's order value.

I guess this sounds kind of complicated but in practice it's really easy...in fact, WAY easier than I originally anticipated. If you want to see the code in action email me at rhinocerous (at) gmail [dot] com and I can send you a login for the CMS.

-Rhino

Last edited by Rhino (2007-08-21 14:26:49)

Lord, give us the wisdom to utter words that are gentle and tender, for tomorrow we may have to eat them.   -Rep. Morris Udall

Re: Found a drag/drop demo from Nathan Smith, but how to save?

Actually it looks like your code is pretty close as is. Try changing it thusly:

		$('ul#list_1').Sortable(
			{
				accept: 'sortable_1',
				activeclass: 'sort_background',
				hoverclass: 'sort_background',
				helperclass: 'sort_placeholder',
				opacity: 0.7,
				tolerance: 'intersect',
					onStop : function()
					{
						serial = $.SortSerialize('list_1');
						var data = serial.hash
						alert(data);
					}
			}
		);
		$('ul#list_2').Sortable(
			{
				accept: 'sortable_2',
				activeclass: 'sort_background',
				hoverclass: 'sort_background',
				helperclass: 'sort_placeholder',
				opacity: 0.7,
				tolerance: 'intersect',
					onStop : function()
					{
						serial = $.SortSerialize('list_2');
						var data = serial.hash
						alert(data);
					}
			}
		);

and see what happens. Don't forget the comma after "tolerance: 'intersect'," or the script will blow up.

Lord, give us the wisdom to utter words that are gentle and tender, for tomorrow we may have to eat them.   -Rep. Morris Udall

Re: Found a drag/drop demo from Nathan Smith, but how to save?

Hi guys,

But how to save this alert data? Lets say I have a db table with:
tab_id | tab_text | tab_list | tab_order

tab_id = the id of the tab
tab_text = the text it show on the tab
tab_list = the listname the tab is in, so in the left column or the right (tab_list = list_1 OR tab_list = list_2)
tab_order = place in the list (3 or 5 or 7 etc)

It's not clear to me yet, I am kinda a noob at programming so plz be as clear is you can get!

Thank you for your patience!

_Null

Last edited by Null (2007-08-22 04:25:40)

Re: Found a drag/drop demo from Nathan Smith, but how to save?

when you initially build your draggable lists, you set the ID of each LI to the tab_id of the respective item. serial.hash will return a POST string containing all of these ID's in the current order on the screen. Then you can use PHP like I mentioned above, but more specifically (again, assuming you have a POST variable named $ord that is your serial.hash, a list of tab_ids):

		$count = 50;
		foreach($ord as $o)
		{
	                update tab_table
                        set tab_order = $count
                        where tab_id = $o
                        $count += 50;
		}

Replace my pseudocode with a real query and you should be off to the races.

Lord, give us the wisdom to utter words that are gentle and tender, for tomorrow we may have to eat them.   -Rep. Morris Udall

Re: Found a drag/drop demo from Nathan Smith, but how to save?

And what does $count = 50 do?

And how does it update tab_list? Cause a tab can be dragged from list_1 to list_2

_Null

Last edited by Null (2007-08-23 00:47:33)

Re: Found a drag/drop demo from Nathan Smith, but how to save?

1. you set $count = 50 before your loop (or 1, or 5, doesn't matter; the only thing that matters is that it's incremental). You'll notice that the last line in the foreach loop is

 $count += 50;

Therefore every time you update a row you then increment the count by 50 and set the next row equal to that new value, then increment again, and so on. So say you had a list with 4 items in it, their tab_orders would end up being 50,100,150,200. Then the next time you retrieve those values from the DB you can tack on an orderby clause in your query (orderby tab_order desc or asc) and they will be all sorted out for you.

2. To update tab_list you will need to pass that as a parameter from your javascript to your php. Expanding on your code that I modified and posted earlier:

		$('ul#list_1').Sortable(
			{
				accept: 'sortable_1',
				activeclass: 'sort_background',
				hoverclass: 'sort_background',
				helperclass: 'sort_placeholder',
				opacity: 0.7,
				tolerance: 'intersect',
					onStop : function()
					{
						serial = $.SortSerialize('list_1');
						var data = serial.hash
						alert(data);
                                                a_php_function(data, 'list_1');
					}
			}
		);
		$('ul#list_2').Sortable(
			{
				accept: 'sortable_2',
				activeclass: 'sort_background',
				hoverclass: 'sort_background',
				helperclass: 'sort_placeholder',
				opacity: 0.7,
				tolerance: 'intersect',
					onStop : function()
					{
						serial = $.SortSerialize('list_2');
						var data = serial.hash
						alert(data);
                                                a_php_function(data, 'list_2')
					}
			}
		);

You have to have some means of transferring your variables from javascript (clientside) into PHP (serverside). I'd assumed that you already know how to do this. There are a variety of different ways to do it, I happen to use jQuery's AJAX functionality ( http://jquery.bassistance.de/api-browser/#ajaxMap ) to call a Codeigniter controller function which in turn calls a model function to do the heavy lifting, then the controller returns the result string. This is by far the easiest way I have found to do it but it won't work for you unless you're using Codeigniter.

Then again, you could achieve much the same results without using Codeigniter. Using jQuery's AJAXmap function you could call any PHP script, including one you have already written to do these queries on it's own if you have one. It doesn't matter what kind of PHP you call through AJAX, it just matters that you echo out the return variable so the AJAX responder can see it and return it to jQuery.

Hmm, theoretically you could also format the data into a GET string then do a window.location  = "www.your-domain.com?" + get_string and have the PHP function read the data in from the URI. I've never tried that method, though I bet it would work.

-Rhino

Lord, give us the wisdom to utter words that are gentle and tender, for tomorrow we may have to eat them.   -Rep. Morris Udall

Re: Found a drag/drop demo from Nathan Smith, but how to save?

Hi,

Thx for the help. but onstop causes the script to update itself after a drop. I want to update both list at the same time using the save button and only then. Not auto save ondrop.

Also, isn't there a better way to save the order? I mean using count is need, but it will boost the nr's very fast. Can't you just reset the order and save/update it then?

Let me explain, at start we have:
id=1 Movable 1 order = 0
id=2 Movable 2 order = 1
id=3 Movable 3 order = 2

Now I am drag and dropping till I have this:
id=2 Movable 2 order = 1
id=1 Movable 1 order = 0
id=3 Movable 3 order = 2

Now on save we reset the order and update it so we get this:
id=2 Movable 2 order = 0
id=1 Movable 1 order = 1
id=3 Movable 3 order = 2

Now I can order by "order" and keeping the nr's small. Perhaps this should be done in php?

_Null

Re: Found a drag/drop demo from Nathan Smith, but how to save?

Ok I have some old code that did this. But it aint working with jQuery. PErhaps you guys know how to addept it:

It are parts fromt my php file (the parts that matter):

Part 1 the db

$query = "CREATE TABLE `$bbdb->menu` (
		`item_id` INT(3) NOT NULL AUTO_INCREMENT,
		`item` varchar(50) NOT NULL default '',
		`set` varchar(50) NOT NULL default '',
		`page` varchar(50) NOT NULL default '',
		`location` varchar(50) NOT NULL default '',
		`order` int(9) NOT NULL default '0',
		PRIMARY KEY  (`item_id`)
		);
		";

Part 2

// Action: add technical stuff before the adminheader loads
function bb_sort_data() {
	
	function parse_data($data) {
	  $containers = explode(":", $data);
	  foreach( $containers as $container ) {
		  $container = str_replace(")", "", $container);
		  $i = 0;
		  $lastly = explode("(", $container);
		  $values = explode(",", $lastly[1]);

		  foreach($values as $value) {
  			if($value == '')
  				continue;
			  $final[$lastly[0]][] = $value;
			  $i ++;
		  }

	  }
		return $final;
	}
	
	function update_db($data_array, $col_check)	{
	  global $bbdb;

	  foreach($data_array as $set => $items) {
		 $i = 0;
		 foreach($items as $item) {
		   $item = mysql_escape_string($item);
		   $set  = mysql_escape_string($set);
		   
		   $bbdb->query( "UPDATE `$bbdb->menu` SET `set` = '$set', `order` = '$i'  WHERE `item` = '$item' $col_check" );
		   $i++;
		 }
		 
	  }
	}
	
	// Let's setup ajax
	if (!isset($AJAX_INCLUDED)) {
	
		$ajax_export_list = array();
	
		function ajax_init() {
		}
		
		function ajax_handle_client_request() {
			global $ajax_export_list;
			
			$mode = "";
			
			if (! empty($_GET["rs"])) 
				$mode = "get";
			
			if (!empty($_POST["rs"]))
				$mode = "post";
				
			if (empty($mode)) 
				return;
	
			if ($mode == "get") {
				// Bust cache in the head
				header ("Expires: Mon, 26 Jul 1997 05:00:00 GMT");    // Date in the past
				header ("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
				// always modified
				header ("Cache-Control: no-cache, must-revalidate");  // HTTP/1.1
				header ("Pragma: no-cache");                          // HTTP/1.0
				$func_name = $_GET["rs"];
				if (! empty($_GET["rsargs"])) 
					$args = $_GET["rsargs"];
				else
					$args = array();
			}
			else {
				$func_name = $_POST["rs"];
				if (! empty($_POST["rsargs"])) 
					$args = $_POST["rsargs"];
				else
					$args = array();
			}
			
			if (! in_array($func_name, $ajax_export_list))
				echo "-:$func_name not callable";
			else {
				echo "+:";
				$result = call_user_func_array($func_name, $args);
				echo $result;
			}
			exit;
		}
		
		$AJAX_INCLUDED = 1;
	}
	
	// End
	
	ajax_init();
	ajax_handle_client_request();
	
	if(isset($_POST['order'])) {
	  $data = parse_data($_POST['order']);
	  update_db($data, "AND (`set` = 'inactive' OR `set` = 'active')");
	  // redirect so refresh doesn't reset order to last save
	  header("location: admin-base.php?plugin=bbmenu_admin_page");
	  exit;
	}
}

add_action('header.php', 'bb_sort_data');

Part 3 the form

function bbsort_form() {
  global $bbdb;

  $sortable_left = "";
  $sortable_right = "";

  // Force (array) to avoid foreach errors
	$r1 = (array) $bbdb->get_results( "SELECT * FROM `$bbdb->menu` WHERE `set` = 'inactive' ORDER BY `order` ASC" );
	foreach( $r1 as $rw )
		$sortable_left .= "<li id=\"$rw->item\"><div id=\"$rw->item_id\">$rw->item</div></li>";

  // Force (array) to avoid foreach errors
	$r2 = (array) $bbdb->get_results( "SELECT * FROM `$bbdb->menu` WHERE `set` = 'active' ORDER BY `order` ASC" );
	foreach( $r2 as $rw )
		$sortable_right .= "<li id=\"$rw->item\"><div id=\"$rw->item_id\">$rw->item</div></li>";

?>
	<h2>Menu Management</h2>
	<h3>Update menu information (drag & drop or double-click the text)</h3>
		
	<div id="col1">
	<strong>Available menu links</strong>
	</div>
	
	<div id="col2">
	<strong>Current menu</strong>
	</div>
	
	<br />
	
	<ul id="inactive" class="sortable box-left">
	   <?php echo $sortable_left; ?>
	</ul>
	
	<ul id="active" class="sortable box-right">
	   <?php echo $sortable_right; ?>
	</ul>
	
	<form action="" method="post">
	  <input type="hidden" name="order" id="order" value="" />
	  <p class="submit alignleft"><input type="submit" onclick="getSort()" value="Update" /></p>
	</form>

That's it thes code parts need to be adepted to be compatible with jQuery. the first and tird part don't need any editing (I think), but the sec part does. I hoped jQuery could cut down the code lines (the ajax ones at least), but I had no succes. I can't post my intire code, cause it's part of a plugin I made.

Experimenting myself, I did manage to use some jQuery code. Only the saving of the order (like is done so good in this non Jquery code) fails.

See all posts above, used the demo by Nathan Smith to create the two tables. I also tried this from the interface demo:

<script type="text/javascript">
	$(document).ready(
		function () {
			$('a.closeEl').bind('click', toggleContent);
			$('div.groupWrapper').Sortable(
				{
					accept: 'groupItem',
					helperclass: 'sortHelper',
					activeclass : 	'sortableactive',
					hoverclass : 	'sortablehover',
					handle: 'div.itemHeader',
					tolerance: 'pointer',
					opacity: 	0.7,
					onChange : function(ser)
					{
					},
					onStart : function()
					{
						$.iAutoscroller.start(this, document.getElementsByTagName('body'));
					},
					onStop : function()
					{
						$.iAutoscroller.stop();
					}
				}
			);
		}
	);
	var toggleContent = function(e)
	{
		var targetContent = $('div.itemContent', this.parentNode.parentNode);
	};
function serialize(s)
{
	serial = $.SortSerialize(s);
	alert(serial.hash);
};
</script>

I can drag and drop, but still not save :(

_Null