I like using ajaxy interfaces in my web apps, and as such I also like the type of status messages that you can achieve using Mochikit. However, sometimes you need to pass a status message between pages (when you’re using raise cherrypy.HTTPRedirect() for example) and I just don’t like having two different ways to display things.
To dissolve my perfectionist worries that the standard tg_flash message in a div just doesn’t cut it, I looked at ways of making the message look the same as a status message from my ajax calls.
Updated on 24/11/05 as per Bob’s suggestions.
Updated on 26/11/05 to fix a JavaScript bug in Internet Explorer
Updated on 16/12/05 to fix some typos as pointed out by Tim
Updated on 12/01/06 to reflect changes to the TurboGears jsonify API
Preparing the way
First, I have my simple div to contain the message within the body tags of my master.kid:
<div id="statusmessage"></div>
Then I created the CSS to make this message box look nice:
#statusmessage {
position: absolute;
top: -1px;
left:200px;
right: 200px;
z-index: 5000;
}
#statusmessage div {
width: 400px;
margin: 0px auto;
height: 50px;
padding: 35px 10px 10px 55px;
background-repeat: no-repeat;
background-position: left;
font-size: 18px;
opacity: .75;
filter: alpha(opacity=75);
}
#statusmessage div.success {
background-color: #99CC99;
border: 1px solid #006633;
background-image: url("/static/images/dialog-information.png");
}
#statusmessage div.error {
background-color: #C00;
border: 1px solid #600;
background-image: url("/static/images/dialog-error.png");
}
Note the last two blocks of CSS. The background image, and colours of the status message can be modified depending on the type of status message. The only ones I’ve defined here are ’success’ and ‘error’, but you can have any number of message types.
The javascript workhorse to control the display of these messages is a simple function using the spanky Mochikit functions available to you.
function displayStatusMessage(status, msg) {
swapDOM("statusmessage", DIV({'id': 'statusmessage'}, DIV({'class': status}, msg)));
callLater(5, swapDOM, "statusmessage", DIV({'id': 'statusmessage'}, null));
}
What this function does is to swap out your empty statusmessage div for one packed with the info you give it. It then waits for 5 seconds before making it empty again.
As you can see, providing you have the CSS for the status type, you can pass any type of status that you can think of.
Using with AJAX calls
To use these status messages with your AJAX calls is a cinch. Here is an example callback function from the app I’m developing:
function saveHandler(result) {
data = evalJSONRequest(result);
if (data['status'] == "success") {
displayStatusMessage(data['status'], data['msg']);
getElement('articleId').value = data['id'];
} else {
if(data['status'] == "error") {
displayStatusMessage(data['status'], data['msg']);
} else {
displayStatusMessage('fatal', 'There was an unknown error!');
}
}
}
The result parameter to the saveHandler function is an XMLHttpRequest object as I’m POST’ing the request to the server rather than using GET. If you want to use Mochikit’s loadJSONDoc() function to handle the request it returns a javascript array for you so you just leave the first line of the above function out and change the parameter name from result to data.
This will provide you with a nice little message everytime you save a document, provided you return the variables status and msg along with the rest of your data in your controller function.
Using with tg_flash
This is all fine and dandy when you’re displaying a status message after an async callback as you have some JSON to stuff the status message with. “How do I use this with tg_flash”, I hear you cry?
Since JSON is native to JavaScript you can actually create javascript objects from any JSON formatted string. Considering how close JSON syntax is to python’s own lists and dictionaries, you should have no problem creating a string in your controller and passing it to tg_flash.
To provide an example I’ve created a controller function called testflash in my app. We need to import a few extras in your controllers.py:
from turbogears import jsonify
And the example for transferring a JSON string in tg_flash:
@turbogears.expose()
def testflash(self):
flashData = {"status": "error", "msg": "The page was NOT saved!"}
turbogears.flash(jsonify.encode(flashData))
raise cherrypy.HTTPRedirect(turbogears.url("/"))
The key here is the string passed to turbogears.flash(). This is a JSON string. JavaScript can recognise this string and build a javascript object out of it. To do this I have the following code within my master.kid’s head tags:
<script type="text/javascript">
function flashedStatusMessage() {
var flashData = getElement('flashTransport').value;
if (flashData != "") {
statusmsg = eval('(' + flashData + ')');
displayStatusMessage(statusmsg['status'], statusmsg['msg']);
}
}
addLoadEvent(flashedStatusMessage);
</script>
I then need to add a hidden element in my master.py’s body tags:
<input type="hidden" id="flashTransport" name="flashTransport" py:attrs="value=tg_flash or ''" />
The script will check for the value of the flashTransport element on every page load. If it fails to find anything there is no message, but if tg_flash has been called the value (already nicely escaped by Kid) will be used as the data for the status message.
You can use this method to insert extremely complex status and error messages into your app, not to mention a myriad of options for styling said messages. You are only limited by your ability to create JSON strings to feed tg_flash and your ability to translate those JSON strings into DOM elements using Mochikit.
Here are some screenshots of the (roughly) styled messages when they pop up in my app:

SuperJared | 23-Nov-05 at 7:23 pm | Permalink
I like what you’ve done. This has made the tgflash that much more dynamic! Though, I’d like to see tgflash support the passing of a CSS class.
Splee | 23-Nov-05 at 8:41 pm | Permalink
Yes, passing a CSS class would be handy, but bearing in mind the way tg_flash stores it’s data in a cookie that may be difficult to impliment.
Bob Ippolito | 24-Nov-05 at 3:09 am | Permalink
Your flash data is technically not valid JSON. Single quoted strings are not part of the spec. While it happens to work because of the way it’s processed, you should probably fix the example.
Ideally you’d be using an API instead of string mangling to generate the JSON document. That way formatting it correctly is someone else’s problem and it’s easier to test that way.
I also have a feeling that your use of Kid interpolation in the script tag is going to have edge cases. You really shouldn’t do that either.. what I do is I make a hidden input field with the value as the data, and just grab it out from the script. That way, you don’t have any generated code at all and the escaping behavior is definitely already correct since it’d be normal HTML attribute value escaping.
Splee | 24-Nov-05 at 9:49 am | Permalink
Excellent, thanks for the tips Bob. I’m not all that hot at JavaScript so these little details are things that I miss. I’ll get that sorted out soon and update the examples.
Tim Diggins | 16-Dec-05 at 2:44 am | Permalink
small point, but the css in your example has one error “#rdsmsg” should be “#statusmessage”. (similarly the ajax callback calls “displayRdsmsg” when it should call “displayStatusMessage()”
Also I’m not sure why you need the call to jsonify within the json.write() within the controller code. I’m running tg0.8 and don’t have a jsonify - but the json.write seems to do a reasonable job anyway…
Many thanks for the write-up.
Splee | 16-Dec-05 at 5:02 pm | Permalink
Tim,
Thanks for pointing out the typos. As you can see I modified this from the project I’m currently doing, so I changed the names to anonymize it a bit… it seems I missed a few ;)
As for using jsonify, yes it is a bit superfluous now, but previously there were some issues with passing numbers that weren’t integers. SQLObject was returning long ints for some ID’s. Basically I put the jsonfiy bit in to make sure that if I (or other people) were to pass more info than just a string that json.write wouldn’t barf.
I think this has been fixed recently, but I’ve left it there as a safety net.
Asif | 28-Apr-06 at 5:57 am | Permalink
how cani use it with asp.net 2.0 using Visual Studio 2005
Please help the Message Box Seems Preety cool and i would like to use it in my web Pages
Thanks
Splee | 28-Apr-06 at 2:19 pm | Permalink
This article was written for use with the TurboGears web framework (http://www.turbogears.org). However, you can create the messages directly using asp and use the same (or similar) javascript and CSS as I have used here.
Chris Arndt | 08-Jul-06 at 6:56 pm | Permalink
Here’s a small improvement for the displayStatusMessage function that allows the user to click on the message to get rid of it and also adds a third parameter for the hide timeout (defaults to 0, i.e. the message stays until user clicks it away).
/*
* display flash message
*/
function displayStatusMessage(status, msg, timeout) {
var msg = swapDOM(’statusmessage’, DIV({’id’: ’statusmessage’},
DIV({’class’: status}, msg)));
if (timeout) {
msg.hidecb = callLater(timeout, swapDOM, msg,
DIV({’id’: ’statusmessage’}, null));
}
connect(msg, ‘onclick’,
function(e) {
var msg = e.src();
if (msg.hidecb) {
msg.hidecb.cancel();
}
disconnect(msg);
swapDOM(msg, DIV({’id’: ’statusmessage’}, null));
}
);
}