Nick Carroll Software Professional

Improving Django Comments User Experience with AJAX

I really like Django. It is not bloated like a lot of other frameworks, and it has a healthy balance between convention and configuration. As a developer I want to be able to use the tools that I want to use, and not be forced into a specific way of doing things. An example of this in Django is the comments framework that is part of django.contrib. The comments framework provides the infrastructure for attaching comments to any domain model in your Django project through content types. It also provides a few spam prevention features that you should consider leveraging, such as a security hash or a hidden honeypot field. However, the default comments form is rather bare, and the workflow for posting a comment requires too many clicks and redirects.

There are some solutions that try to improve the user experience, and do a good job of it, but require hacking the comments framework. I personally don’t like hacking the internals of any framework. Not because I’m scared to, but because I don’t want to inherit any unnecessary maintenance overheads.

Besides, I believe the comments framework does the right thing. It doesn’t try to do too much, and by that I mean the HTML that it generates for the default rendered form and corresponding responses can easily be mashed up as part of the submitting pages DOM. So with a bit of JQuery and AJAX knowhow you can post comments without refreshing or being redirected away from the submitting page.

First I use the utility methods from the comments framework to list all comments for a particular discussion content type, and to display the default comment form immediately after. Note that the comment form is wrapped in div tags with the id “comment_form”. The div will be used to override the block with responses from the server after an ajax post.

<h3>Comment</h3>
    {% load comments %}
    {% get_comment_form for discussion as form %}
    <div id="comment_form">
    {% render_comment_form for discussion %}
</div>

In my template (discussion.html) in which I want to allow people to comment on something I add the following to a script block that will insert the JavaScript into the head element of the base template (base.html).

 1 <script type="text/javascript" charset="utf-8">
 2 function bindPostCommentHandler() {
 3     $('#comment_form form input.submit-preview').remove();
 4     $('#comment_form form').submit(function() {
 5         $.ajax({
 6             type: "POST",
 7             data: $('#comment_form form').serialize(),
 8             url: "{% comment_form_target %}",
 9             cache: false,
10             dataType: "html",
11             success: function(html, textStatus) {
12                 $('#comment_form form').replaceWith(html);
13                 bindPostCommentHandler();
14             },
15             error: function (XMLHttpRequest, textStatus, errorThrown) {
16                 $('#comment_form form').replaceWith('Your comment was unable to be posted at this time.  We apologise for the inconvenience.');
17             }
18         });
19         return false;
20     });
21 }
22 
23 $(document).ready(function() {
24     bindPostCommentHandler();
25 });
26 </script>

In line 2 I define a method called bindPostCommentHandler() which performs the ajax call to post a comment and to handle the response when the form submit event is triggered.

In line 3 I remove the preview button from the form as I do not want this functionality. Rich text plugins generally have a preview mode, so I don’t see the need for the server to process a preview request.

In line 4 I bind the ajax post call to the submit event of the comments form. This means when I click on the Post button to submit the form I will trigger an ajax post instead of a normal post to the server.

Line 7 serialises the data from the form input fields using the JQuery serialize() function.

Line 8 specifies the URL to post to. I use the utility method from the comments framework to retrieve this for me, so I don’t need to hard code it to a specific URL. The commentformtarget should retrieve the correct URL for the comments application that you should have configured in your urls.py file. If not then add the following URL route to your urls.py file.

(r'^comments/', include('django.contrib.comments.urls')),

Line 10 specifies that the response will be HTML. This is necessary so that JQuery knows how to parse and handle the response.

Line 11 details the success callback, which is the function that calls when the response is successful. The callback simply replaces the form in the div wrapper with the response. A successful response could either be a “thank you” message for posting a comment, or a form displaying fields with invalid fields. Line 13 is necessary to rebind the bindPostCommentHandler() function to any new form that appears in the div wrapper. If you omit this rebinding, then you will lose ajax posts on successive submits.

Line 15 defines the callback that gets called when the server responds with an http error code. I just override the div wrapper block with an apologetic message.

Finally, line 23 binds the bindPostCommentHandler() function when the page is ready.

As you can see from the above, all you need is some JavaScript to improve the user experience for posting comments using Django’s comments framework. No framework hacking necessary. Note that if you want to further improve this solution, then you can add sliders, fade ins, and fade outs for displaying the responses from the server. I left these out for brevity. I also left out appending the submitted comment to the list of comments, as the above solution will only display the submitted comment after the user performs a GET request after the submit.

Configuring the Grails Root Application Context

By default if you create a Grails application called funkysite (i.e. you did grails create-app funkysite on the command line), then when you run your application the root context is set to /funkysite. This means that your controllers such as a UserController with a show action will be available at http://localhost:8080/funkysite/user/show. Having “funkysite” as the root context is redundant, especially if you want to host your application at http://funkysite.com/funkysite/user/show.

My preference is to just have “/” as the root application context. Fortunately, you can easily configure the root application context in Grails. All you need to do is edit conf/Config.groovy and add grails.app.context = “/”. This will set your root application context to “/”, so that the above UserController and show action will be available at http://localhost:8080/user/show.

IR Pen version 2

My previous attempt at building a simple IR pen did not work as expected. The power source was too weak for the IR LED, and consequently the Wiimote had difficulty detecting the IR light.

For my second attempt I purchased an IR LED, some wire, a momentary switch, a 10 Ohm resistor, and a battery holder for two AA batteries. I also had a whiteboard marker lying around, which I used to create the casing for the IR pen.

Image 1

I wired up the circuit so that the IR LED was in serial with the switch, resistor and battery holder. I then cut off the nozzle from the whiteboard marker casing. I needed space to fit the switch, so I cut the cylinder shaped casing in half, and drilled a hole to push the switch through. I then popped the LED through the nozzle, and wrapped the casing around the circuit. Everything was then held in place using electrical tape. It was a prototype, so I didn’t bother with aesthetics, which the rubber band holding the pen and battery holder together clearly shows.

I am glad to report that this particular IR pen works perfectly with my laptop screen and the Wiimote. However, I will need to test it out on a projected image from a data projector before officially giving the thumbs up.

Simple IR Pen for Wiimote Whiteboard

My previous foray into using a Wiimote with my laptop led me down the path of building a USB sensor bar so that I can use the Wiimote to control the pointer movements. This approach worked better than expected, but it doesn’t work so well if you want finer control of your mouse pointer. For example, when I was demonstrating the Wiimote integration with my laptop I was quite nervous about the demo not working, and this was made apparent by the shaky lines that I was drawing with the Mouse Gestures. As a result some of the Mouse Gestures did not register.

A better approach would be to do what Johnny Lee did with the Wiimote to create the Wiimote Whiteboard. Johnny Lee used the Wiimote as an IR camera pointed at a projector screen, and created a pen with an LED which the Wiimote can track. This approach provides for more accuracy and smoother movements of the pointer.

The barrier of entry to the Wiimote Whiteboard is creating the IR pen. Johnny Lee suggests wiring up a circuit containing an IR LED, momentary switch, resistor, and power supply, then shoving it into a pen. If you google “IR pen” you will also come up with some complicated solutions. One guy even tried to cram the circuit into a highlighter casing.

Image 1

My solution is really quite straightforward. In fact you only need to go to your local electronics store and pick up two items: an LED keyring torch; and an IR LED. When purchasing an LED keyring torch, make sure that you can easily replace the LED. I used this LED keyring torch from Jaycar Electronics. I then pulled the torch apart, pulled out the LED, and replaced it with an IR LED. This solution meant I didn’t have to do any soldering or fiddling around. It all fit together into a nice compact form factor that cost me less than $10, and took no longer than 10 minutes to switch the LED.

Custom built USB Sensor Bar

I had a few days off work to recharge the batteries and was looking forward to heading to the beach, but ended up taking a rain check due to the bad weather. Sydney just had the wettest Summer in years, which was good in a way as it ended up breaking the drought and putting water in the dams. So to make the most of my time off I decided to build my own USB Sensor Bar so that I could get a Wiimote working with my laptop.

If you followed my previous post on getting the Wiimote to work with Ubuntu, then you should be able to move the cursor around using your Wiimote, and using the A and B buttons as left and right clicks respectively. However, using the accelerometer alone for moving the cursor around does not make for a great user experience. To enable the Wiimote to work more effectively you need to setup a point of reference that can be used by the Wiimote driver to calculate the directional movement of the Wiimote accelerometers. This point of reference for the Wii is the Sensor Bar that sites on top of the television set. So you can either buy a battery powered Sensor Bar or make your own USB Sensor Bar. I ended up doing the latter by following the instructions at Terbidium.

To get started you need a USB cable, four infrared LEDs, LED holders, aluminium tubing, electrical tape or heat shrink tubing, and a laptop.

Image 1

The USB Sensor Bar is a simple serial circuit that consists of four infrared LEDs that are grafted to an old USB cable.

Image 2

You may need to add a resistor into the circuit if your LEDs don’t produce a voltage drop of 5 Volts, which is the standard power source for USB devices. It is worth testing your circuit design on a circuit board as shown below.

Image 3

The circuit is then squeezed into a tight-fitting aluminium tubing that is cut to about 30cm in length. The USB cable hangs out one end of the tubing, and the LEDs sit in LED holders that have been positioned into some neatly drilled holes. The completed USB Sensor Bar is pictured below.

Image 4