Category Archives: Transfer ORM

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.

Getting to know Transfer ORM


I realize this post comes at an awkward time.  It seems most of the posts about ORMs nowadays all center around Hibernate after the release of ColdFusion 9.  However, if like me, you are using some of the open-sourced CFML engine alternatives like Railo or OpenBD, Hibernate support might be on the project road map but it is not here yet.  I still have quite a few sites out there running Transfer ORM, so I for one will be supporting it as long as it is available.

Read the rest of this entry