Using jQuery sortable() to set display sort order


I am currently working on wrapping up a web site for a client that has quite a few image and video galleries each with 100+ images.  One of the biggest problems I have had with the galleries is being able to sort how they were displayed for the end users.  Initially I added in buttons to move the items up and down in the sort, but when you are dealing with a lot of items it can be tedious and aggravating work.

This weekend things came to a head because the client was getting annoyed.   They wanted a way to quickly update the item sort so I went about finding the best way to go about it because I love happy clients.  My main concern was having items in the database with the same sort order and possibly throwing off the sort.  As it turns out, the answer was surprisingly simple.

The web site in question is already using the jQueryUI so modifying the image administrator to use the UI sortable() option was really no hassle what-so-ever.  I am using AJAX pretty heavily on the site I just needed to do some editing to my file list page that is included on the page with an AJAX load call.

<cfscript>
 catObj = application.transfer.get("categories",url.id);
 base = '/path/to/web/root';
 filePath = base & catObj.getBaseDirectory();

 cq = application.transfer.createQuery("FROM images WHERE images.cat=:id ORDER BY images.sortorder");
 cq.setParam("id",url.id,"numeric");

 files = application.transfer.listByQuery(cq);
</cfscript>

<cfif files.recordcount>
 <div id="sortableContainer" style="margin-top: 48px;">
  <cfoutput query="files">
  <div id="#ID#" style="width: 600px; margin-bottom: 12px;">
   <div style="height: 20px; text-indent: 12px; padding-top: 4px;">Image Header</div>
   <div class="ui-widget-content">
    <table style="width: 600px;">
     <tr>
      <td width="75">
      <a href="/image_viewer.cfm?id=#ID#" target="_blank" style="color: blue;">
       <img src="/#catObj.getBaseDirectory()#/thumbs/#filename#" width="75" height="75"/>
      </a>        
     </td>
     <td>
      <form id="img#ID#" action="ajaxProxy.cfc" method="post">
       <input type="hidden" name="postType" value="updateImage" />
       <input type="hidden" name="id" value="#ID#" />
       <input type="text" name="Title" value="#Title#" style="width: 200px;" /><br />
       <input type="submit" name="Method" value="Update" />
       <input type="button" value="Delete" onClick="void(0);" />
       <input type="button" value="Set" onClick="void(0);" />
      </form>        
     </td>
    </tr>
   </table>
  </div>    
 </div>
 </cfoutput>
 </div>

 <div style="padding-bottom: 128px;"/>

<script type="text/javascript">
 $('#sortableContainer').sortable({
 stop    : function(event, ui){
 var result = $('#sortableContainer').sortable('toArray');
 var sortOrder = '';

 for(var i=0; i<result.length; i++){
 sortOrder += result[i];
 if(i < result.length - 1) sortOrder += ',';
 }
 setImageSortOrder(sortOrder);
 }
 });

 function setImageSortOrder(order){
 $.ajax({
 url            : 'ajaxProxy.cfc',
 data            : {
 method        : 'setImageSortOrder',
 sortOrder     : order
 },
 async        : false,
 success        : function(data){
 var incoming = $.parseJSON(data);
 if(incoming.svrStatus == "-1"){
 alert(incoming.svrMessage);
 }
 }
 });
 }
</script>
</cfif>

Basically this is just looping through the database and outputting all the images in divs in the div named sortableContainer which has the sortable() interaction applied to it.   I also bind to the stop event which will fire when a sortable child is released which will build a string containing the image id’s in the proper sort order.  I pass this value to the setImageSortOrder() function which makes an AJAX call to my remote proxy which will update the values in the database.

<cffunction name="setImageSortOrder" access="remote" returntype="struct" returnFormat="JSON">
 <cfargument name="sortOrder" type="string" required="yes"/>
  <cftry>
  <cfscript>
   var response = structNew();
   var imageObj = "";
   var sortCnt = listLen(arguments.sortOrder);
   var i = 0;

   for(i=1; i lte sortCnt; i++){
    imageObj = application.transfer.get("images", listGetAt(arguments.sortOrder, i));
    imageObj.setSortOrder(i);
    application.transfer.save(imageObj);
   }
   response['svrStatus'] = "0";
   response['svrMessage'] = "Video sort order updated successfully.";
  </cfscript>
  <cfcatch type="any">
   <cfscript>
    response['svrStatus'] = "-1";
    response['svrMessage'] = cfcatch.Message;
   </cfscript>
  </cfcatch>
 </cftry>
 <cfreturn response/>
</cffunction>

The function setting the sort order in the database is pretty straight forward.  I am using Transfer ORM for my database interactions, but it would be easy enough to covert the example into a query.  Since the jQuery function is passing a list of the image id’s in the correct order I loop through the list and set the sort order of each image to its index in the list.  No need to worry about items having the same sort order and displaying incorrectly.  It is pretty much fool proof.

Also of note, this code makes use of the $.parseJSON() function added in jQuery 1.4.1.  No more having to worry about whether or not a browser can support JSON.parse().  This in itself is a good reason to switch to a newer version of jQuery if you have not already.

Advertisements

About Robert Zehnder

Web application developer specializing in ColdFusion/Railo and Open Source development.

Posted on March 29, 2010, in ColdFusion, jQuery, jQuery UI, Railo, Transfer ORM. Bookmark the permalink. 12 Comments.

  1. thanks, I was looking something similar in PHP but this post gave me some ideas and I can do the same in PHP now. Cheers.

  2. I am a beginner……
    I am doing a igoogle panel implementation in a website….
    I would like to know how Sortable works on Postback?

    I have placed 8 datagrids(ASP.Net) in 8 portlets(div) and 2 columns(div), like this

    And the sortable is working fine…….
    I am trying to implement the Igoogle panels, where the user can
    save his settings, but using a save button……
    So when i randomize the order and save it i am able to get the sorted order
    and saving userid and preferences(sort string) to the database
    How to call the sortable with this string?
    How can I pass the arguments to sortable?

    Thanks in advance,
    Som

    • If I understand you correctly (it is late in the day 🙂 ) it looks like you have figured out how to save the sortable display, you are just needing help displaying the divs in sorted order when the screen is reloaded?

      In the demo above I am appending the image to the end of the sort so I really didn’t have to worry about where it was inserted. Without testing I really don’t have a definitive answer, but off hand I can see a few possible solutions. First, if you inject your divs into the sortable container the same as the saved sort order. Along the same lines, if the code for the containers was static you could loop through your divs and manually move them to the right place. So for instance you have #div1 and #div2 and you know that with the user sort #div1 comes after #div2 you could say:

      $(‘#div1’).insertAfter(‘#div2’);

      Just iterate through your sort until they are all placed correctly. I will play around with this idea tomorrow at lunch and see if I can come up with a working demo.

  3. thank you so much for your reply…..
    I already have the sorted string with me,
    say the original order is div1,div2,div3,div4…..
    Sorted order is div3,div2,div1,div4……
    I saved this sorted order into a string and saved it to the database,
    but when the page reloads, the order reverts back to original order
    i.e, div1,div2,div3,div4……
    I also have the sorted string on postback, but I dont know how to
    call the sortable function using the list div3,div2,div1,div4……

    Thanks in advance,
    Som

  4. Hey thanks for the idea….
    looks like a js error on the demo page….

  5. I saved this sorted order into a string and saved it to the database,
    but when the page reloads, the order reverts back to original order
    i.e, div1,div2,div3,div4……

    • Hey Hostina,
      I too had the same problem…..
      It reverts back to the order in which you have defined your tags,
      IF:
      1) you have a Javascript error.
      2) you have a syntactical error in your html code like forgetting to close a tag etc….
      check your code again, hope it solves your problem.

  6. Hey Hostina,
    I too had the same problem…..
    It reverts back to the order in which you have defined your tags,
    IF:
    1) you have a Javascript error.
    2) you have a syntactical error in your html code like forgetting to close a tag etc….

    check your code again, hope it solves your problem.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: