Meh @reading (but Yay to reading on the iPod)
I can't be the only programmer who finds reading a chore? And what about those 5-billion page programming tomes? Am I the only one who never finishes those?
Blog of Titus Stone; Part programmer, part... wait... what?
I can't be the only programmer who finds reading a chore? And what about those 5-billion page programming tomes? Am I the only one who never finishes those?
Posted by titus at 11:55 PM 0 comments
Labels: haskell, ipod, javascript, kindle, RWH
It seems this question has been coming up a lot on Stack Overflow, and I wrote a pretty lengthy response which I'll include here. The original question is #1883266.
Q: I'm building a web application that needs to have content on the page updated in real time without a page refresh (like a chat or messaging application). How do I do this with Django? In other words, how do I use AJAX with Django?
A: There's a lot going on in order to make this process work...
This can be confusing when you're first starting because it's not always clear what the client does and what the server does, but if the large problem is broken down I think you'll find it's a simple process.
If the client is going to regularly poll the server for new chat entries, then the server (django) needs to have some type of API to do so. Your biggest decision will be what data type the server returns. You can choose from: rendered HTML, XML, YAML, or JSON. The lightest weight is JSON, and it's supported by most of the major javascript frameworks (and django includes a JSON serializer since it's that awesome).
# (models.py)
# Your model I'm assuming is something to the effect of...
class ChatLine(models.Model):
screenname = model.CharField(max_length=40)
value = models.CharField(max_length=100)
created = models.DateTimeField(default=datetime.now())
# (urls.py)
# A url pattern to match our API...
url(r'^api/latest-chat/(?P<seconds_old>\d+)/$',get_latest_chat),
# (views.py)
# A view to answer that URL
def get_latest_chat(request, seconds_old):
# Query comments since the past X seconds
chat_since = datetime.datetime.now() - datetime.timedelta(seconds=seconds_old)
chat = Chat.objects.filter(created__gte=comments_since)
# Return serialized data or whatever you're doing with it
return HttpResponse(simplejson.dumps(chat),mimetype='application/json')
[
{
'value':'Hello World',
'created':'2009-12-10 14:56:11',
'screenname':'tstone'
},
{
'value':'And more cool Django-ness',
'created':'2009-12-10 14:58:49',
'screenname':'leethax0r1337'
},
]
<div> tag which we'll call <div id="chatbox"> which will hold whatever the incoming chat messages are. Our javascript simple needs to poll the server API that we created, check if there is a response, and then if there are items, append them to the chat box.<!-- I'm assuming you're using jQuery -->
<script type="text/javascript">
LATEST_CHAT_URL = '{% url get_latest_chat 5 %}';
// On page start...
$(function() {
// Start a timer that will call our API at regular intervals
// The 2nd value is the time in milliseconds, so 5000 = 5 seconds
setTimeout(updateChat, 5000)
});
function updateChat() {
$.getJSON(LATEST_CHAT_URL, function(data){
// Enumerate JSON objects
$.each(data.items, function(i,item){
var newChatLine = $('<span class="chat"></span>');
newChatLine.append('<span class="user">' + item.screenname + '</span>');
newChatLine.append('<span class="text">' + item.text + '</span>');
$('#chatbox').append(newChatLine);
});
});
}
</script>
<div id="chatbox">
</div>
Posted by titus at 9:20 AM 0 comments
Labels: ajax, django, javascript
I spent several hours this week trying to track down a bug with programmatically selecting text via javascript.
if (textarea.setSelectionRange) {
// Set selection for Mozilla-ish browsers
textarea.setSelectionRange(start, end);
}
else {
// Set selection for IE
var range = textarea.createTextRange();
range.collapse(true);
range.moveEnd('character', start);
range.moveStart('character', end);
range.select();
}
start = beforeSelect.length;That doesn't always work!
end = start + select.length;
Posted by titus at 11:02 PM 0 comments
Labels: internet-explorer-fail, javascript
I've been using Dana Robinson's WMD Editor fork for a couple of months now. It was a great place to start. The problem is, it's becoming a real pain to customize. Small things were taking huge amounts of time to accomplish so I had a tough decision to make: Continue to use WMD Editor and have to spend hours for customization that should only take a minute or two -OR- re-write the UI myself on top of the existing Markdown rendering component?
Introducing: MarkEdit -- the Javascript MarkDown editor built on jQuery (to replace WMD Editor). It's currently being hosted on BitBucket.



Posted by titus at 4:55 PM 0 comments
Labels: javascript, markedit, wmd-editor
The jQuery UI library provides a really useful method: dialog. It allows any tag to be rendered as a fake "window" in the browser in either a modal or non-modal way. It's easily a natural choice for any time we need a dialog to fetch input from a user.
What makes it somewhat difficult for some programmers to use is that it's an asynchronous function. It opens the dialog then keeps on executing code. Here's why this can be confusing.
Let's say for example we're building a web-based instant messaging client. A feature of this client would be the ability for the user to change their nickname, even after they've already logged in (similar to how MSN works). We've decided to implement this functionality by adding a button on the IM toolbar "Change Nickname". When the user clicks it, the dialog should popup, ask them for their new nickname, and then perform whatever action should be taken to update the nickname.
The Natural-But-Doesn't-Work Method
The natural way (using synchronous code) to achieve this would be something like the following:
(Javascript code is included above. I'm not sure if this renders via RSS)
But as you can see from the code, this won't work. Why? Dialog is an asynchronous method. It's easy to get comfortable with synchronous dialogs, after all, Javascript has a built in one: input(); However in this case we need to develop a new strategy for working with an asynchronous dialog.
Now lest you be tempted to do something like while(!closed) { // do nothing }; let me just ask you now: stop!. That's fighting the system. That will most likely lock up the browser, and it's an absolute waste of resources.
Using Callbacks to Trigger Behavior
Stepping back from the problem, what we really want is that we have some code, and we want it to run only when the user has finished using the dialog boxes. Being asynchronous, the dialog function actually provides a measure for doing so: Callbacks. When we defined the OK and Cancel buttons, we specified an anonymous function() that would be called whenever that button was clicked. This is a call back. We can use this exact same mechanic to signal our other code when it should run.
But how? Here's some updated code...
(Javascript code is included above. I'm not sure if this renders via RSS)
To accomplish this would could create a "gating" function -- a function which be responsible for dispatching either a call to our dialog, or a call to the code which should be run when we have a value. This is implemented in the example code as getAndSetNewNickname. In addition to the new method we've also added a parameter to the getNewNick method: callback. This parameter will allow us to pass a function that will be called whenever the "Ok" button is clicked. In our implementation, this function happens to be the same function that's calling it. The situation we end up with is a recursive callback which will call our function a 2nd time when it has a value.
The end result? Our setNewNickname method only gets called when we actually have a value. You've successfully gotten user input, and didn't have to restore to timeouts or a weird while loop solution.
Happy jQuery'ing!
Posted by titus at 11:12 AM 0 comments
Labels: javascript, jquery
Kudos to Google and MIT for uploading such great quality stuff to YouTube (if you've been scared off by the drones of webcam-yourself-complaining-about-current-events then give it another try).
Posted by titus at 8:11 PM 0 comments
Labels: javascript, jslint
© Blogger template 'Minimalist G' by Ourblogtemplates.com 2008
Back to TOP