Ajax fun!!! (Drag and Drop Reordering)
Drag and Drop reordering is a really useful thing to have. The original motivation is to let merchants using Karova Store reorder the way in which articles, products display on their sites, but as I was implementing it we found it easier to basically let them reorder anything that could be displayed in a table.
Theres lots of examples and guides online to show you how to us the scriptaculous libraries to do this, but they all seem to deal with li’s within ul’s not tables.
The tech I’m using for this is pretty simple stuff. The html is coming from an xslt, the data’s going to be held in an xml file, the client side scripting is basic javascript and the server side ajax post back is being handled in c#. All of the things we’re going to do here can be done in lots of different languages, as its likely your setup is going to be different from this.
So heres what we want to do:
- Display the items in a table,
- Be able to reorder them by just dragging and dropping them
- Record what the new order is whenever it changes
- Use this new order when displaying them to the end user
Follow the white rabbit
1. We need libraries
No, we’re not going to be writing this from scratch, so we need the real engines that are going to do most of the work. So run off and get the latest version of the scriptaculous libraries. We’re using version 1.6.5, but anything recent should work.
Assuming you’ve got them now, just include the the prototype and scriptaculous libraries in your header, making sure the prototype reference is before the scriptaculous reference. They’ll import everything else they need as long as they’re in the same directory.
2. Make up your mark up
For the libraries to work properly we need to have our table to have the right id’s and so on. To use the default markup that works with the scriptaculous libraries mark it up with the tbody having an id of ‘item_list’ and the individual tr’s having id’s of ‘item_{ID}‘.
Basically all you need for it to work is the container to have an id of item_list (you can change this later on) and the individual items with and id of the form item_ID. If you really want to change structure of the individual id’s on the ’s you can, but you’ve got to go and change the scriptaculous libraries, so if I were you, I’d stick with this, at least for now.
3. Initial Dragging and Dropping
Before we go ahead and get all the ajax posting sorted, lets just check it works in its current form. Somewhere in your code you need to put the following javascript to tell the libraries to work their magic.
Sortable.create('item_list',{tag:'tr', ghosting:true,constraint:'vertical'})
Oh, and this needs to be put in AFTER the table. Otherwise it won’t work. Got that? After, as in further down, later, nearer the end. Ok?
Its the Sortable.create() that does the magic, the first argument is just the id of the thing your reordering, while the things inside the braces are optional arguments. You can find a list of the extra options you can add on wiki page for sortable.create.
Basically the three we’ve got here do the following things:
- ‘tag’ – the tag, by default its taken as li, so this only needed if its different, as it is here for use in a table
- ‘ghosting’ – setting this to true displays a transparent version of what you are moving under your mouse
- ‘constraint’ – tells it house we’re moving the elements. Here its up and down, thus vertical, simple really
We’ll add some more later but for we just need to see that it works.
4. Recording the Reordering
What good is it if the user moves all the items around and but we have no record of what’s changed. No good. Thats how much good it’d be. We could have the user hit a confirm button when their done reordering, but thats and extra click. Of course we could just record what the new order as and when it changes.
Which is what we’ll do. Don’t panic, it’s surprisingly easy.
All we need to do is add a new argument to our sortable.create. This ones called ‘onUpdate‘, and we use it like this:
Sortable.create('item_list',{tag:'tr', ghosting:true,constraint:'vertical', onUpdate : updateOrder});
With the updateOrder function looking like this:
var options = { method : 'post', parameters : Sortable.serialize('item_list') };
For some reason this didn’t work for me. It did return a serialized list yes, but not one in a format I could use. But don’t fret. If this has not worked for you either, try this (longer more scary looking) version.
var ampcharcode= '%26'; var serializeOpts = Sortable.serialize('item_list') + unescape(ampcharcode) +"key=item_list; var options = { method : 'post', parameters : serializeOpts };
For this to work totally right we also need to add this new part to our sortable.create function…
Sortable.create('item_list',{tag:'tr', ghosting:true,constraint:'vertical', onUpdate : updateOrder, tree:true})
There, that bit at the end. Don’t ask me why it helps it work, but it does. Just take it and be thankful.
Hold Up! Wheres this ajax you were promising? Oh wait, here it comes, I was just building it up a bit.
So, we now have all the information, all we need to do is pass it along to our server so it can do some tricks with it. For this we need to add the line ‘new Ajax.Request(’reorder.aspx’,options);‘. Where ‘reorder.aspx is the name of the this you’re posting to.
This is what our finished bit of javascript should look like:
function updateOrder(){ var ampcharcode= '%26'; var serializeOpts = Sortable.serialize('item_list') + unescape(ampcharcode) +"key=item_list; var options = { method : 'post', parameters : serializeOpts }; new Ajax.Request('Reorder.aspx',options); } Sortable.create('item_list',{tag:'tr', ghosting:true,constraint:'vertical', onUpdate : updateOrder, tree:true})
If you want to debug at this point, all you need to do is add an alert(options.parameters) before the ajax request.
Now – onward onto the server side fun!
6. Actually using this stuff
Remember, we’re doing this in c#, so if you’re not doing it in c#, you don’t really need to follow all this too carefully.
The posted things come through to the c# as a form, but for safety, lets check it the form has keys with a simple test.
if(System.Web.HttpContext.Current.Request.Form.HasKeys()) { //do some stuff here }
Simple. Child’s play. Now lets fill it up with everything.
The stuff we get out of the seriazable comes back in the form ‘item_list[1][id]‘ with the key being the position. To get out exactly what we need lets use a regular expression to get out the numbers and leave everything else.
System.Xml.XmlDocument docO = new System.Xml.XmlDocument(); docO.LoadXml(""); string itemKey = System.Web.HttpContext.Current.Request.Form.Get("key"); foreach (string key in System.Web.HttpContext.Current.Request.Form.Keys) { if(key.IndexOf(itemKey) > -1) { System.Text.RegularExpressions.Match m =System.Text.RegularExpressions. Regex.Match(key,itemKey+@"[(?d+)][]"); if(m.Groups["position"]!=null) { System.Xml.XmlElement posEl = docO.CreateElement("position"); String itemOrder = m.Groups["position"].Value; posEl.SetAttribute("pos",itemOrder); Sring itemId = System.Web.HttpContext.Current.Request.Form.Get(key); posEl.SetAttribute("id",itemId); docO.DocumentElement.AppendChild(posEl); } } }
As you can see here, the c# takes the parameters that are passed and puts them into an xml document, ending up with some xml of the form:
7. The Finished product
So we’re done. Quickly, lets go over what we’ve got.
Using the prototype and scriptaculous libraries we can reorder lines in a table. When ever we do, a function called ‘updateOrder‘ gets called. This serializes the new sequence in our table and posts it through to some waiting c# for some server side handling. On the server side we take the serialized sequence, do some reg ex clean up then generate some xml with it, which can then be used however we want.
Its all good.
As ever, if you notice any problems with anything above, let me know and I’ll go through and fix it. Let me know if this was useful to you.
You can a working example of what we’ve done here all packed up nicely for you. Have fun!