Posted in jQuery, ColdFusion | Posted on 02-25-2009 | 6,621 views
Earlier today my coworker mentioned the need for a way to easily move items up and down on a web page. In this case the idea was to sort a list of documents. We've probably all done this before. You list out each item and the one on top has a down arrow, the one on the bottom has an up arrow, and all the rest have both up and down controls. Turns out - and no big surprise here - there is actually a cool little jQuery utility to make this a bit simpler - the Sortable control.
If you follow the link to the demo (and please do, it's rather slick!) you will see how nice this control works. Instead of slowly clicking items up and down, a user can simply drag and drop the order they want. A big +1 for usability here (in my opinion). What isn't so obvious though is how you persist these changes. Let's dig a bit into the control and I'll show you how you can tie it to ColdFusion.
First, let's look at a simple version that just does the sorting:
2
3<head>
4<script src="jquery-1.3.1.js"></script>
5<script src="jquery-ui-personalized-1.6rc6.js"></script>
6<script>
7$(document).ready(function(){
8 $("#sortable").sortable();
9 });
10</script>
11</head>
12
13<body>
14
15<h1>Test</h1>
16
17<ul id="sortable">
18 <li>First</li>
19 <li>Second</li>
20 <li>Third</li>
21</ul>
22
23</body>
24
25</html>
I absolutely love this. Seriously. I want to form a domestic partnership with jQuery and never look at another framework again. You can see a demo of this here. But this is just a front end demo. Simple and sexy, but we need something real, and a lot of times that is where things can break down. I began by making the data dynamic.
2<cfloop index="x" from="1" to="5">
3 <cfset queryAddRow(data)>
4 <cfset querySetCell(data, "id", x)>
5 <cfset querySetCell(data, "title", "Title #x#")>
6</cfloop>
Normally that would be set in my controller code or a CFC at least, but you get the idea. I then changed my UL/LI:
2 <cfoutput query="data">
3 <li id="item_#id#">#title#</li>
4 </cfoutput>
5</ul>
Ok, not rocket science, but you get the idea. I then added a button:
and modified my document.ready:
2 $("#sortable").sortable();
3 $("#saveBtn").click(persist)
4 });
My persist function will take care of saving the data. The docs for Sortable are pretty extensive. What we want is the serialize function. As you can guess, it will serialize the sortable data. It does this by grabbing the id values from the items you had marked as sortable. My persist function looks like so:
2 console.log('running persist....')
3 var data = $("#sortable").sortable('serialize')
4 console.log(data)
5}
When run, the console reports:
item[]=3&item[]=2&item[]=1&item[]=4&item[]=5
Kind of an odd format. You can change it up a bit by passing additional parameters to the sortable call, but you get the basic idea. A demo of this version may be found here. Obviously you need Firebug installed and open to see the console messages.
Alright, so let's take it a step further. As I said, I thought that format was a bit odd. Sortable supports serializing to arrays as well:
and we can tie this to an Ajax call
2 console.log(txtStatus)
3 })
At this point I ran into a bit of trouble. I forgot that you can't send a complex data structure 'as is' over the wire. jQuery was smarter than me in this case and simply converted the data back into a list, much like the first serialize example. This time though it was just a list of values:
item_N,item_X,item_Z
My CFC would have to parse the list and note both the position and the ID value from each item:
2 <cfargument name="order" type="any" required="true">
3 <cfset var x = "">
4 <cfset var id = "">
5 <cfset var item = "">
6
7 <!--- loop through and make a new order --->
8 <cfloop index="x" from="1" to="#listLen(arguments.order)#">
9 <cfset item = listGetAt(arguments.order, x)>
10 <cfset id = listGetAt(item,2,"_")>
11 <cflog file="ajax" text="setting id #id# to position #x#">
12 </cfloop>
13
14</cffunction>
Normally this would actually run a query, but I think you get the idea. I've included my sample code as an attachment.
I have to admit - I thought the 'interactions' section of jQuery UI wasn't that exciting, but I'm beginning to see some real benefit here.


You'll run into trouble if you setup more than one list of items to sort between as the serialize method will not pick up the parent 'ul' in it's output.
Just a tip if you get more advanced with your lists..
I like that you can also format your HTML like this
<div id="beers">
<div>Yuengling</div>
<div>Tenentt's</div>
<div>JW Dundee's Homey Brown</div>
</div>
And still have the innder <div>s be the items that can be sorted.
I think Bill is saying that if you have a nested lists, there may be a problem, just a guess
p.s. Working on a Progress Bar demo today.
For that situation, I ended up replacing the nested sort with a technique (also powered by jQuery) I originally developed for reordering items on a web page when using an iPhone or iPod Touch (since drag-and-drop isn't really an option there) that lets my users move an item with just two mouse clicks. You can find the original blog post about the technique here:
http://www.swartzfager.org/blog/index.cfm/2008/3/2...
I've been using jQuery a lot in my applications, also, I find it very easy to use, even insanely simple at times. I did some earlier work with Spry, but jQuery just seems so much easier to code and maintain.
http://home.cfproject.co.uk
Jonny
Just thought I'd point out that this is a fairly sweet table sorter:
http://kryogenix.org/code/browser/sorttable/
using what the author calls unobtrusive javascript. its very customizable and fairly fast.
regards,
larry
I have a problem with database. Could you please provide a little CF code, which would save reordered list into database. I can't figur eit out.
Plaese help! Many thanks!
update tblFoo
set sortorder = #NEWORDER#
where id = #THEID#
neworder would be the new sort order, and THEID the primary key. In my example above, it's #id# and #x# in the cflog. Oh, and you would use cfqueryparam of course.
In jQuery 1.3.1, toArray produces the parameters
sortedIdArr 2504
sortedIdArr 2471
sortedIdArr 2472
Source
sortedIdArr=2504&sortedIdArr=2471&sortedIdArr=2472
(From Firebug)
and now with 1.4.2 it's change to
sortedIdArr[] 27
sortedIdArr[] 26
sortedIdArr[] 28
Source
sortedIdArr%5B%5D=27&sortedIdArr%5B%5D=26&sortedIdArr%5B%5D=28
The brackets seem to break my coldfusion code that loops through the list of values. I get an error that says "Element SORTEDIDARR is undefined"
add
jQuery.ajaxSettings.traditional = true;
to your document ready function.
YES!
Thank you. You just made my day.
Nando
i fought with this problem for hours yesterday. Thanks for sharing your solution.
And thanks Raymond, for this excellent tutorial (and the rest of your blog as well).
[Add Comment] [Subscribe to Comments]